How to keep track of users who are not logged in Django? - python

I am writing a simple e-commerce web app with Django.
I want Anonymous Users (unregistered guests) to still be able to add items to their cart and proceed to checkout without having to register or log in. I am having trouble figuring out how to actually keep track of them (according to best practices/with security in mind), if these types of users don't have an identifiable username/id that Django provides.
Concretely, suppose I have these models:
class Shirt(models.Model):
"""Represents the type of item I am selling."""
...
class OrderItem(models.Model):
"""Represents an individual entry in an Order."""
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
item = models.ForeignKey(Shirt, on_delete=models.CASCADE)
quantity = models.IntegerField('Quantity', default=1)
class Order(models.Model):
"""Represents an order, i.e. a 'collection' of entries."""
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
items = models.ManyToManyField(OrderItem)
When I work with logged in users, my models' fields get populated with corresponding user objects (that logic is already implemented with the help of request.user). Of course, that doesn't work with unregistered people.
I have already read some info on django sessions and cookies, but I am not quite sure I get how to make it all work in a nice and clean way.
I am just starting out with web development (and Django, by extension), so I would greatly appreciate some advice!

Related

Django - Team/User relationships

I'm at a loss... I'm just learning Django and I am really rather confused about how to make a field work the way I would like it to.
I understand that Django has a native "Groups" model. However, I am looking to build my own teams model for customization and practice.
Here is my models.py file for my Users app:
from django.db import models
from django.contrib.auth.models import User
class Team(models.Model):
members = models.ManyToManyField(User)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
admin = models.BooleanField("Admin Status")
Here's where I'm confused. I would like to be able to call the team that the user is part of directly from User.Profile. So, I want to add a field to my Profile class that will automatically populate with the team name when a user is added to a team.
A potential problem I can see is that, currently, I can assign a user to multiple teams. This doesn't bother me, perhaps I can have a Profile model field that automatically populates with a list of all the teams that the user is associated with. Regardless, I can't figure out what type of field I would need to use, or how to do this.
Does that make sense?
A potential problem I can see is that, currently, I can assign a user to multiple teams.
Indeed, you can however easily retrieve the Teams the myprofile object is a member of with:
Team.objects.filter(members__profile=myprofile)
You thus can make a property for the Profile model:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
admin = models.BooleanField("Admin Status")
#property
def teams(self):
return Team.objects.filter(
members__profile=self
)
Then you thus access the Teams of a myprofile with myprofile.teams.
So, I want to add a field to my Profile class that will automatically populate with the team name when a user is added to a team.
From my limited knowledge of database, you can add a name field to your Team model.
Keeping in mind your requirement as mentioned in question, i would suggest you to use django reverse relations to get all the teams the profile is associated with
user_teams = User.objects.get(id='user_id').profile_set.all()[0].team_set.all()
to know more about django ORM reverse relation, here is a very short article

Use new subclass with already in use database in django

I'm trying to create a customized User class inheriting from django User. Problem is I already have some users in database which can not be deleted whatsoever and also I have another class (let's say reports) which have a foreignkey to User. My question is: Is there any way to create my new User class and keep the old data too?
thanks in advance.
You can create related model that links back to User. This is a common approach if you have different types of users, but there are also other use cases.
class SpecialUserProfile(models.Model):
user = models.OneToOneField(User, null=True, blank=True, on_delete=models.SET_NULL)
special_feature = models.CharField(max_length=100, null=True, blank=True)
etc.
What you also need to do is create this profile, when new user is added to User. You can do this with post_save signal.
#receiver(post_save, sender=User)
def create_special_user_profile(sender, instance, created, **kwargs):
if created:
SpecialUserProfile.objects.create(user=instance)
You create profiles for existing user with a command or write and run a temporary function that does that for existing users in User.
Now you can use ORM in the sense of user.specialuserprofile.special_feature.
This way you'll keep using User model as a base, it won't mess with build-in user related functionalities, won't have think about new and old users and you can use this new model for any additional information about users.

Uncertain how to design django apps - best practices

In every django app I've previously developed I used only one app, which had all models, forms etc included.
It was OK for small apps, but now it's time to do it right way :)
I want to have auth module which will cover such things like user signups, login, welcome email sending etc.
So... I've created app called 'auth'. In auth.models module I have something like this:
class BaseModel(models.Model):
uuid = UUIDField()
time_created = models.DateTimeField(auto_now_add=True, null=True)
time_modified = models.DateTimeField(auto_now=True, null=True)
class Meta:
abstract = True
class Team(BaseModel):
title = models.CharField(_('Team title'), max_length=64)
class Member(BaseModel):
user = models.OneToOneField(User)
team = models.ForeignKey(Team)
USER_ROLES = (
('admin',_('Administrator')),
('member',_('Team member')),
)
role = models.CharField(_('User role'), max_length=6, choices=USER_ROLES, default='member')
Here comes my first trouble. Every user in my app will have one-to-one Member object. Member object stores some additional data about the user. Every single user must be assigned to group (which I call Team)... But wait... Should't be Team class moved to separated app which will manage Teams???
Also, I like to inherit all model classes from BaseModel. Do I have to add this same BaseModel class in every app again? Isn't a breaking "don't repeat yourself" rule?
Also, during registration of new user I need to create new Team for him. Where should I put code for this? Its related both to user management and team management.
I would need some guidance, how to fix these design issues.
Apps are intended to be self contained "modules" of code that could be reused in other django projects in the future. Ask yourself whether you'd ever want to re-use this code in another project.
Your extended user model, with teams would strike me as being part of a single app. You're not defining site-specific behaviour with respect to teams or members. I could also see many cases where you'd re-use the above code.

Querying for followers in news-feed-based data model in Django

I have this following (simplified) model in Django which is very similar to the Pinterest data model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
class Collection(models.Model):
owner = models.ForeignKey(User,related_name='collection_owner')
followers = models.ManyToManyField(User, related_name='collection_followers', null=True, blank=True, default = None)
class Item(models.Model):
collections = models.ManyToManyField(Collection,blank=True,null=True)
I have a User model, a UserProfile model that maps 1-1 with a user, a Collection model which has an owner and followers and items that can be part of multiple collections. I'm struggling with determining how to execute the following queries in Django:
Get all the followers of a given user. The definition of a follower is one that follows at least one collection that is owned by that particular user.
Get all the distinct items of the collection a user follows.
I'm not sure if I can do these in a single query or do I have to break it up in several queries? What would be the best approach and are there any trade offs?
Thanks for any help.
The first would be something like this I think:
User.objects.filter(collection_owner__owner='the user')
The latter should be something like this:
Item.objects.filter(collections__followers='the user').distinct()
You should be aware however that these type of queries do not scale to large amounts of data. Doing that will require quite a bit of hacking...

Giving anonymous users the same functionality as registered ones

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?
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.
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.
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.
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.

Categories