Django - models with relations to possibly nonexisting users - django

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?

Related

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

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?

The right way to set up permissions for "workspaces" in Django?

I am trying to create a site where users work together in "workspaces". A workspace is like an account for a group of users.
Each workspace will have a number of users. There are two user groups: "superusers" and "regular users".
A user may be a member of more than one workspace. The user can never belong to both user groups in the same workspace, but the user can be a "superuser" in one workspace and a "regular user" in another.
I am trying to find out whether I can create this type of setup using the out-of-the-box permissions framework in Django, or whether the best solution is to create my own supporting tables?
My own solution would be a data model like this:
#(assume default User model)
class Workspace(models.Model):
workspace_name = models.CharField(max_length=100)
class WorkspaceUser(models.Model):
user = models.ForeignKey(User)
workspace = models.ForeignKey(Workspace)
usertype = models.CharField(max_length=100) #for simplicity, would only have two options
Is this a sensible way to do it, or can I utilize the out-of-the-box framework to achieve the same instead?
No, the built-in permissions framework is just for the model level - ie "user can modify workspaces generally" rather than "user can modify this specific workspace".
Your solution looks sensible. One addition would be to add an explicit many-to-many declaration using the WorkspaceUser as the through table:
class Workspace(models.Model):
workspace_name = models.CharField(max_length=100)
users = models.ManyToManyField(User, through='WorkspaceUser')
This doesn't change the table structure but gives you the ability to do my_user.workspace_set.all() to get all workspaces for a user or my_workspace.users.all() to get all users for a workspace.

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.

webapp instances approach

I'm wondering about how can I modeling a webapp in order to provide services to several clients at same time, running like a instance for every one.
In example if I'm providing a basic erp software for a client, and I want give this service
to everyone that want use it online with their own data...
How can I do that?
I'm thinking in a main webapp like a gateway to login the users and route them to their
own webapp, but, is really needed a approach like this? with my approach do I need a db for every
client?, and maybe it will be a big mess...
What another approach can I apply for this case?
Any comment are welcome..
Each model can easily have a user field which is a ForeignKey to your User model. Each model instance could then be associated with a user and each user would log into a single webapp with a single database. Of course your views must then handle filtering and authenticating access to each model instance:
class Resource(models.model):
name = models.CharField(max_length=20)
someattr = models.FloatField()
user = models.ForeignKey(User)
def show_resources(request):
resources = Resource.objects.filter(user=request.user)
render_to_response("view.html", {'resources': resources})

Django Auth, Users and Permissions

I have a Organization and Employee models
class Organization(models.Model):
is_active = models.BooleanField()
name = models.CharField(u'Name', max_length = 255)
...
class Employee(models.Model):
user = models.OneToOneField(User)
organization = models.ForeignKey(Organization)
...
Will it be good if I use AUTH_PROFILE_MODULE, so Employee becomes user profile?
This way I can use Django permission system to set employees permissions, like
can see all documents
can see own organization documents
can see his own documents
Is is OK to have a permissions that are not a global one like "can see all documents"?
And what If I also want to have a permissions per Organization? How to do this? And how to distinguish permissions per Organization and per Employee?
Edit: I'm using Django 1.4
In short, yes, you're ok.
Because:
1) Using AUTH_PROFILE_MODULE=Employee will make Employee instance to be available for instance in this way:
def view(request):
employee_instance = request.user.get_profile()
2) Using custom permissions is easy, see: https://docs.djangoproject.com/en/dev/topics/auth/#custom-permissions
Edit:
having custom permissions on organizations is possible as well, probably best if you create permissions programatically, like mentioned in the manual, this way:
content_type = ContentType.objects.get(app_label='myapp', model='Organization')
permission = Permission.objects.create(codename='can_do_something', name='Can Do something',
content_type=content_type)
now, you have permission aware organization model, you just assign it to your user.
To clarify more:
Django auth system is sort of a fixed ACL. You assign roles to a user (or group) and that's pretty much it. Django offers helper wrapper function to easily filter out users who don't have a given permission. If you need to decide at runtime and/or in more generic way, whether an object has permission to do something, you either need full blown ACL system (and which django.auth is not) or you code that kind of behavior yourself. This depends on your needs and obviously on the need to manage those permissions. In the OP's case, the behavior is fixed, therefore I would recommend just coding this in and be happy. But the needs may vary and so does the solution. Django auth is good at assigning static permissions to user, gropu or a "profile" object. What that means to your app is up to you in the end.
So in this case, the good solution would be to have a fixed set of permissions like "can view own documents" or "can view organization documents" that is assigned to user/group. And you app should decide, what it means and serve documents accordingly, taking either runtime state in the account or using models structure to determine the proper data set to serve.