I'm building a student registration system by using django where students are registered. Students can be viewed only by their class teachers and school principals based on object level permission. There are School, Class and Student models. Each school can have more than one school principals and each class can have more than one class teachers.
There will be two object level permissions:
School principals will be able to see all of the students registered to their school. They won't be able to see students of other schools.
Class teachers will be able to see the students that are registered to their classes. They won't be able to see students registered to other classes in the same or different schools.
I have searched through various 3rd party django libraries to implement such a hierarchical user group architecture. I have seen django-groups-manager, but it is a bit complicated for my issue. Then, I decided on django-mptt's registration of existing models feature and come up with model implementations such as:
from django.contrib.auth.models import Group
from django.db import models
import mptt
from mptt.fields import TreeForeignKey
TreeForeignKey(Group, on_delete=models.CASCADE, blank=True,
null=True).contribute_to_class(Group, 'parent')
mptt.register(Group, order_insertion_by=['name'])
class School(models.Model):
"""School object"""
name = models.CharField(max_length=255)
group = models.ForeignKey(
Group, related_name='school', on_delete=models.CASCADE)
class Class(models.Model):
"""Class object"""
name = models.CharField(max_length=255)
group = models.ForeignKey(
Group, related_name='class', on_delete=models.CASCADE)
school = models.ForeignKey(
School,
on_delete=models.CASCADE
)
class Student(models.Model):
"""Person object"""
fullname = models.CharField(max_length=255)
class = models.ForeignKey(
Class,
on_delete=models.CASCADE
)
In this way, each School and Class objects will have their own Groups, school group being parent of the classes' groups of that particular school. So I can now create school principal users by using django's User and assign it to the related parent group. By the same way, I can also create teacher users and assign them to their children groups of their class objects. And when a school principal or class teachers want to view their registered students, I can apply object level permission by filtering to their user groups.
My question is, is it the right way to do it? Is creating one group per each school/class objects meaningful?
Thanks to nigel222's suggestion, instead of using built-in django groups, I have overriden built-in django User model. I have also followed this tutorial which was really helpful. My latest version of models.py is as follows (Don't forget to override default User class, you need to let django know about it by adding AUTH_USER_MODEL = 'myapp.User' to your settings.py file):
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
"""Custom user model with an extra type field"""
USER_TYPE_CHOICES = (
(1, 'superuser'),
(2, 'principaluser'),
(3, 'teacheruser'),
)
user_type = models.PositiveSmallIntegerField(choices=USER_TYPE_CHOICES)
class School(models.Model):
"""School object"""
name = models.CharField(max_length=255)
users = models.ManyToManyField(settings.AUTH_USER_MODEL)
class Class(models.Model):
"""Class object"""
name = models.CharField(max_length=255)
users = models.ManyToManyField(settings.AUTH_USER_MODEL)
school = models.ForeignKey(
School,
on_delete=models.CASCADE
)
class Student(models.Model):
"""Person object"""
fullname = models.CharField(max_length=255)
class = models.ForeignKey(
Class,
on_delete=models.CASCADE
)
Related
I am trying to implement a relationship between three tables; User Project and Permissions
I already have a ManyToMany relationship between User and Project
class CustomUser(AbstractUser):
username = models.CharField(unique=True, max_length=20, null=False, blank=False)
// etc...
class Project(models.Model):
user = models.ManyToManyField(CustomUser, related_name="projects")
Now I am trying to add the Permission Table. So that User can take different roles on different project.
class Permissions(models.Model):
can_read = models.BooleanField(default=True)
can_read_and_edit = models.BooleanField(default=False)
// etc...
What is the best approach on this situation. Should I Add Permission table into manytomany relation as a third table? Or is there a better way to achieve this
You can use a custom through model to add details to the Many-to-many relation like this:
class CustomUser(AbstractUser):
pass
class Project(models.Model):
users = models.ManyToManyField(CustomUser, through='ProjectDetails')
class Permission(models.Model):
pass
class ProjectDetails(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
permission = models.ForeignKey(Permission, on_delete=models.CASCADE)
This way, each user for a project can be assigned a certain permission (per project). You can also add more details about a certain user's involvement in a project. For example, when they started in a project.
If you want to learn more, you can have a read here.
I am just learning Django so I thought of creating a project called job board to understand more in detail. I have drawn the following use case.
People can register as job seekers, build their profiles and look for
jobs matching their skillsets
Companies can register, post jobs.
Multiple representatives from a company should be able to register
and post jobs.
Independent Recruiter can create an account as well.
The company can contact to that independent recruiter.
How would be the model design for such a use case? I am confused with the multiple user types in Django. Some favors creating a user profile, while some favors using Groups.
For now, I could only do the following
class User(AbstractUser):
'''
Abstract user because django recommends to start with custom user
'''
username = None
email = models.EmailField(_("Email Address"), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
def __str__(self):
return self.email
class Company(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
I could create a model for User and Company. But I have no idea on handling multiple user types like user can be either job seeker or recruiter. Also, multiple representatives from a company should be able to register and post jobs as well as there can be independent recruiter as well. How would you handle such a case if you have to? Can anyone help me in a step by step guide, please? This way it will clear my confusion and will help me in better design of tables in the future.
Update with example in a nutshell
class User(models.Model):
'''
User can be of any 3 types or can have multiple role as well
'''
is_job_seeker = models.BooleanField(default=False)
is_recruiter = models.BooleanField(default=False)
is_mentor = models.BooleanField(default=False)
class Company(models.Model):
user = models.ForeignKey(User) # only user with is_recruiter flag active can be
class JobSeeker(models.Model):
user = models.OneToOneField(User)
# job seeker profile related fields like experiences, skills, education, profile image etc
class Recruiter(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
company = models.ForeignKey(Company, null=True, blank=True)
# recruiter related profile
Your implementation is almost there. It doesn't look like you need a custom user model right now, so I would just use Django's default.
I would have something like:
from django.conf import settings
from django.db import models
class Company(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
# Other company-related fields
class JobSeeker(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
# Other jobseeker-related fields
class Recruiter(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
company = models.ForeignKey(Company, null=True, blank=True)
# Other recruiter-related fields
When you create any of the models above, you can assign them a user account; and for recruiter, you can assign the company they work for. For example, a company named stack_overflow can have its own company account with a username/password/etc. Then, recruiters who work for stack_overflow could also have their own accounts with their own username/password/etc. Running a command like stackoverflow.recruiter_set will give you all recruiters who work for stack_overflow.
Note that I do not reference User directly. Using the above approach makes your life easier if you decide to switch User models in the future.
I am assuming you don't want to create a User, then create a Company and link it to that user - you just want to do it in one go. That's a slightly different question and the solution will involve you creating a sign-up Form, or something of that sort, where you can add some logic about whether the user is a company, recruiter or jobseeker.
Regarding your other points, it looks like you're looking to set user permissions. Here are the docs for setting default permissions for your custom users, and here are the general docs for Django's built-in permissions system. For example, your Company and Recruiter model could return True for has_perm('your_app.add_job'), while your Jobseeker model returns False. I.e. Companies and Recruiters can create Jobs, but jobseekers cant.
Hope this helps!
I am creating my own users, Restaurant and Customer. I have extended the AbstractUser class and then created a OneToOneField field for each user. I am wondering if I need to add the AUTH_USER_MODEL in my settings.py. And also wondering what that does exactly...
What I was planning on doing was adding to my settings.py:
AUTH_USER_MODEL = 'myapp.Customer','myapp.Restaurant'
Do I have the right idea here?
My models.py:
class User(AbstractUser):
is_restaurant = models.BooleanField(default=False)
is_customer = models.BooleanField(default=False)
class Restaurant(models.Model):
user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
restaurant_name = models.CharField(max_length=50)
def __str__(self):
return self.restaurant_name
class Customer(models.Model):
user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
address = models.CharField(max_length=200)
def __str__(self):
return self.user.get_full_name()
No. AUTH_USER_MODEL isn't expecting a tuple, so this won't work.
In any case, Restaurant and Customer are not your user model; your subclassed User is. That's what you should be putting in that setting.
I would suggest create single user table instead of three different tables and add type as restaurant, customer, admin etc. And add only one table into settings file. this won't lead any further issues authentication etc. Having single user table is always robust. In your case having three tables seems not good to maintain.
========== UPDATE ===========
Create model for user named as CustomUser (or name which you feel better) and extends to User Model of Django using AbstractBaseUser,PermissionsMixin. like
class CustomUser(AbstractBaseUser): have all fields which user table has already. and add your desired table to bifurcate type of restaurant and
customer have type field with choices option.
For further help you can check section https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#substituting-a-custom-user-model
I have been getting my head around these basics but I am not getting it right. I am trying to associate my view to my user model using team which is a foreign key. When I try to create of a gps, I get an error saying "team is a required field" but instead it should be read only. The team attribute should be filled automatically with the id of the currentUser
Model
class User(models.Model):
first_name = models.CharField(max_length=200,blank=False)
last_name = models.CharField(max_length=200, blank=False)
class Gps(models.Model):
location = models.CharField(max_length=200,blank=False)
team= models.ForeignKey(User, on_delete=models.CASCADE)
serializers
class GpsSerializer(serializers.ModelSerializer):
class Meta:
model = Gps
fields = ('id','location','team')
view
class Gps_list(generics.ListCreateAPIView):
queryset = Gps.objects.all()
serializer_class = GpsSerializer
team = serializers.PrimaryKeyRelatedField(
read_only=True,
default=serializers.CurrentUserDefault()
)
There are two changes needed. First, team field definition should be moved to serializer class instead of view. Second, you should use Django's contrib.auth.User model instead of your definition of User, as because serializers.CurrentUserDefault() will bring request.user only. So you should remove your User definition and import that to your models.py:
from django.contrib.auth.models import User
Further steps would be to replace read_only=True with queryset=User.objects.all() to allow create.
I want to implement a team feature in django 1.8. (Team as in sports team)
Every user can join up to one team at a time and a team thus can hold many users. Now i am unsure how to define my models.py
I started with this core, but now i am unsure how to make the connection of Team<->User
from django.db import models
class Team(models.Model):
name = models.CharField(max_length=64, unique=True)
description = models.TextField(max_length=1024)
logo = models.ImageField()
from django.contrib.auth.models import User
class Player(models.Model):
user = models.OneToOneField(User)
team = ForeignKey('Team')
Do I now create a second class user_team or do I just add the team as a foreign key to the user? (and if thats the way where would i need to do this?)
Thanks,
Wegi
// edit: I added some code at the bottom. Would this Player model be enough to define the relationship?
For this use case, I will still suggest an alternative using a ManyToMany field, with an intermediate model and model manager.
A quick sample structure looks like this:
from django.db import models
from django.contrib.auth.models import User
class Team(models.Model):
name = models.CharField(max_length=64, unique=True)
description = models.TextField(max_length=1024)
logo = models.ImageField()
players = models.ManyToManyField(User, through='Player')
class PlayerManager(models.Manager):
use_for_related_fields = True
def add_player(self, user, team):
# ... your code here ...
def remove_player(self, user, team):
# ... your code here ...
def trasnfer_player(self, user, team):
# ... your code here ...
class Player(models.Model):
user = models.ForeignKey(User)
team = models.ForeignKey(Team)
other_fields = #...
objects = PlayerManager()
Usage:
Player.objects.add_player(user, team, *other_fields)
You will then be able to get User related Team, for example:
team_with_user = Team.objects.filter(players__name="hello")
user_in_team = User.objects.filter(team__name="world")
Note: I haven't tested the code, so please correct me if I make any mistake above.
The reason why I prefer this way is to abstract away your database logic into application. So in future if there is a need for allowing User joining multiple teams, you can just change the application logic to allow it through the manager.
As suggested by #aumo I solved the problem by adding a user profile model like this:
from django.contrib.auth.models import User
class Player(models.Model):
user = models.OneToOneField(User)
team = models.ForeignKey('Team')
I chose this solution over adding teams as a ManyToMany field inside the Teams class because I am not sure if any more field need to be added to the Player during development.
Thanks to everybody for your help.