Django: How to keep non-PII from removed users under GDPR - django

I'm using Django to build a B2B SaaS hobby project, where each company (tenant) can have many users and, eventually, remove them.
It's important to keep the reason why a user was removed.
The straightfoward way for this to be solved would be to turn is_active to False and add a reason_for_removal field on the User model. Then make sure is_active is indexed (is it by default in Django?) and all querysets are appropriately filtered.
However, in a GDPR world, is this the right way to do it? Does it make sense to create a separate model called RemovedUser just with the user and reason_for_removal fields (and no other Personal Identifiable Information) and delete the original user instance?

Related

Django - models with relations to possibly nonexisting users

I am working on an enterprise LMS powered by Django REST framework.
Authentication is done via Google OAuth 2.0 using the package drf-social-oauth2, and the target organizations for my software are schools and universities.
Some models have foreign keys to the user model; however, due to the nature of my application, oftentimes a user may want to reference a user that isn’t present in the database yet. This happens because users are created in the database upon their first login in the application via OAuth, yet the person who wants to create a specific model instance referencing another user may want to do so before they’ve logged in for the first time: for example, a teacher may want to pre-enroll a list of students into their new course, but those users might not have logged in for the first time yet, and therefore might not exist in the database.
I’ll give a concrete example with a model in my application:
class UserCoursePrivilege(models.Model):
"""
Represents the administrative permissions a user has over a course.
See logic.privileges.py for the available permissions.
"""
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name="privileged_courses",
)
course = models.ForeignKey(
Course,
on_delete=models.CASCADE,
related_name="privileged_users",
)
allow_privileges = models.JSONField(default=list, blank=True)
deny_privileges = models.JSONField(default=list, blank=True)
This object is created in the frontend by accessing a table which shows all registered users and allows turning on switches that correspond the specific permissions for that user.
More than once have I found myself in the situation in which a teacher would email me telling me they couldn’t find their colleague to add their permissions for a course, and I would tell them to have them log in first and then come back to find the in the user table.
However, this isn’t very user-friendly and somehow counterintuitive considering that my application doesn’t provide an explicit user creation process, so the mental model for users is that their account somehow “already exists” and they just need to sign in.
I’m looking for a way to handle this in as transparent way as possible.
The target user experience is something like this: if the user cannot find the person they want to create the object for, the interface shows them a banner like “Can’t find the person you’re looking for?” and allows them to type in the email address of that person and proceed like normal (in the example above, that would entail showing them all the toggles to select permissions to grant).
Then, an instance of the correct model would be created, but with a null foreign key to user.
Then I would have a model that looks like this:
class PendingModelInstance(models.Model):
"""
Represents a model instance which references a user that doesn't exist yet
"""
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.TextField()
content_object = GenericForeignKey("content_type", "object_id")
email_address = models.TextField()
an instance of this would be created referencing the “partial” instance with the missing FK and with the email address of the user.
Then, upon user creation, a query is made to retrieve all instances of PendingModelInstance which have the email of the newly created user, their referenced models are then retrieved and updated with a FK to the new user instance.
This approach seems like it could work fine, but it introduces an issue I don’t really like: it makes foreign keys nullable, which they don’t need to be and shouldn’t be.
Is there a better alternative?

Session or Model Field in Django?

I'm a beginner in Django. When developing an app, I want to fulfill the following functionality: There is some pictures in one webpage. A user can 'like' a picture by clicking a button bellow it. But one user can only like a specific picture once.
Now there seem two methods to do this.
1) Set an attribute in the session. So I when a user click a button, I can check if he has already 'liked' this picutre according to this session.
2) Add a new field in my user's model to record which pictures he has 'liked'.
Then I don't know which one to use. My questions are as follows:
For method (1), session can expire after some time (e.g. 2 weeks).
So for a user who revisits my website after 2 weeks, is it true that
I can not prevent him from re-liking the picture he's already 'liked'
before?
If I want to have access to the info about which pictures a user
has 'liked', is it true that I can only use method (2) to store this
information?
Thanks a lot!
If you want the "favorites" to persist across multiple sessions, then yes, you need to store the data somewhere that isn't volatile. A simple solution is to use a separate model, a LikedPicture for example:
class LikedPicture(models.Model):
user = models.ForeignKey(User, db_index=True)
picture = models.ForeignKey(Picture)
Session expired? No problem, just get the ones they've liked from the model. You could take it a step further and make the related model generic, so you don't have to make a separate model to hold each association, if you have several different models you're going to associate similarly.
Want to make sure the user only favorites something once? Django makes this ridiculously easy with get_or_create():
favorited_picture, created = FavoritedPicture.objects.get_or_create(user=user,
picture=picture, defaults={'user': user, 'picture': picture})
I find this method to be much more straightforward than trying to maintain a comma-separated-field on a model to store the ids of the favorited things.

django user model foreign key to company

I am beginning my first django project and would like to know which method is best for creating a relationship between User & Company models.
Option 1 - User existing django user model and create a user profile model
Class UserProfile(models.Model):
user = models.OneToOneField(User)
company = models.ForeignKey(Company)
Option 2 - Create custom user model
Class MyUser(AbstractBaseUser):
"all of the required user data -- left off for brevity"
company = models.ForeignKey(Company)
I have no issue with the base user model, my concern is more related to performance IF the application grows to a larger scale. I realize option 1 requires an additional database hit; however, I like the flexibility of having a UserProfile which can be modified to include additional criteria.
Which option would you prefer, or is there a better way?
I have decided to create the UserProfile to give additional flexibility. While we don't know how the application will grow, we feel it is important to keep our domain as flexible as possible. Thank you for all of the feedback.
So it basically depends on the use case and scope of the application, if you know that you just care about user nmae, email add, pass mainly then i will go with in built User Profile that comes with django. Otherwise, I would prefer making another UserProfile, as you will have the flexibility to add more parameters and methods with respect to your project.

DRY approach to add user information without using Auth.user in Django

I'm writing an application where I need to register user information even if the user does not exists (yet) in Django. Basically I need to insert almost every fields used by Auth.user (I don't care about password). I've also created a Profile model connected with a OneToOneField to the Auth.user model and I need to fill in these fileds as well for users that do not exist yet.
If a user will register later to the site (using Auth.user) I will look for him by email and if I will find him I will merge my inserted information and the ones that he provided.
QUESTION:
What is the best approach to implement user persistence without repeating myself in creating models very similar to each other?
I would recommend using a dummy value in either the date_joined or last_login fields for the User model, if you wanted to implement a solution without adding a new field just to serve as an indicator flag.
The potential problem with using the is_active flag is that you may end up wanting to use this flag to "blacklist" or effectively delete an account without actually removing their record from your database (to prevent re-creation of the account with same credentials, etc.) If you start relying on the fact that the password is not set, then if the day ever comes where you want to implement alternative login methods (OAuth) then your password field will not be set in these cases as well (huge conflict).

Should I ForeignKey to Django User or a Profile model?

I'm wondering what people's thoughts are on joining models directly to the auth.User object vs to the user's profile model.
I'm storing some different types of models which my user are adding in my app. App users will search for other users via criteria on these models.
On the one hand, I'm thinking that if I join straight to User then I won't need to do request.user.get_profile() each time I need to grab the User's records, and it doesn't presuppose that a User always has a profile (they do in my app at the mo, but still). This leaves the profile model as just containing the user's contact details.
On the other hand, I imagine I'll most likely need values from the Profile (eg name, location) when I'm looking up these other models.
No doubt either will work, so maybe it doesn't matter, but I just wondered what other people's thoughts were.
Thanks!
Ludo.
I would also recommend creating foreign-keys to the User model. It just makes your life simpler when working with the user object in the view, for one. So, you can do things like request.user.foo_set, etc. without having to go through the profile model.
In general: If you want to make your apps reusable, always create foreign keys to User model.
As you already said, in most cases you will need User as well as Profile instance, so to prevent multiple database queries, use cache.
If reusability isn't relevant, create foreign key to Profile and use select_related() to get User instance with single query.