I need advice on a multiple user type.
Scenario:
A user can be an organization, where in this organization they can place adverts on the website. The owner of this organization(user) can edit/delete users and adverts of his own organization(group). In this organization user type there are users that also can log in and they can only see the adverts placed by them, but the owner of this group must see all adverts of his own and of his users. Think like an estate listing where an organization has multiple locations/users that can place adverts, and has to be managed by a admin user of this organization.
What type or model is the best/cleanest for implementing this in a good way? Do I need the Django's User and Group model?
One solution would be to have the "User Profiles" structure.
So you use the standard Django User Model and you attach to it several OneToOne relationships depending on the number of profile types you'll have. This has the advantage of allowing users to cover more than one role at the same time.
For example:
from django.contrib.auth.models import User
class Organization(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="organization")
name = models.CharField(max_length=50, blank=True, null=True)
class Supervisor(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="supervisor")
name = models.CharField(max_length=50, blank=True, null=True)
organization = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name="supervisors")
class CustomUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="custom_user")
name = models.CharField(max_length=50, blank=True, null=True)
supervisor = models.ForeignKey(Supervisor, on_delete=models.CASCADE, related_name="custom_users", blank=True, null=True)
And then when you go and create the models for the ads to be displayed on the website you can use the built-in PermissionRequiredMixin.
In order to do that you have to start by adding "permissions" in the ad model Meta class:
class Ad(models.Model):
# fields
class Meta:
permissions = [
('can_edit_ads', 'org_representative')
]
Then on your view you have to extend the PermissionRequiredMixin, example:
class EditAd(UpdateView, PermissionRequiredMixin):
model = Ad
template_name = "ad123.html"
permission_required = "ad.can_edit_ads"
A quick way to test it is by going in the user table on the admin panel, open a user detail page where you can see all the permissions, and there alongside the others you'll find your custom one as well.
From there you can easily assign the new permission to the specific user.
Related
I've created a Many-to-Many relationship for the model UserProfile, to enable users to grant access to a particular feature to one another. The relationship works as expected with the use of symmetrical=False to ensure a user access is one-way.
Model
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phone = models.IntegerField(blank=True, null=True)
image = models.ImageField(upload_to='profile_image', default="default_thumbnail.jpg")
department = models.ForeignKey(DepartmentModel, on_delete=models.SET_NULL, null=True)
allow_booking_access = models.ManyToManyField("self", blank=True, symmetrical=False)
def __str__(self):
return self.user.username
class UserInline(admin.StackedInline):
model = UserProfile
can_delete = False
verbose_name_plural = 'UserAccounts'
class UserAccount(BaseUserAdmin):
inlines = (UserInline,)
I am able to query the users that a particular user wants to grant access to via: (for example id=1)
UserProfile.objects.get(id=1).allow_booking_access.all()
However, I would like to retrieve the users that have granted access to the particular user.
How would I do this?
Additional Information
Using Relation
Database Information
You can filter with:
UserProfile.objects.filter(allow_booking_access=my_user)
With your sample data, it will return the UserProfile with id=7 for this query.
or if you want to query in reverse:
UserProfile.objects.filter(userprofile=my_user)
With your sample data, it will return the UserProfiles with id=7, id=3, user=4 and user=7 for this query.
I'm making a small test project with below user types: School Admin, Teacher, Student, Parent.
And each user type will have different Permissions like School Admin has full access... Parents can only view their Childern's Activity. Teacher can see all students but can add / edit marks for their respective students only. Teachers can setup Exam etc.. Students can take exam and submit the exam, but cannot edit / add any other information. Just can edit his profile detail.
Approach 1: Do i need to create a Custom User calss and apply to each user type (ie one Custome Class.. and 4 user type calss).. and similarly have 4 different view and html pages?
Approach 2: Just have one custome class and have an field with UserType which will have the have as "SchoolAdmin", "Teacher", "Student","Parent".. and one view and html (as the data page would remain same and only data record will restrict), and somehow identify the User Type in View, and filter the record?
Definately some view or html pages will be specific to one user type only which is i am able to handle, the issue is to use same view / html page to handle all user type.
Please suggest... and any code snippet will will more helpful.
# models.py
---------------------
class CustomUser(AbstractUser):
USER_TYPE_CHOICES = (
('SchoolAdmin'),
('Teacher'),
('Student'),
('Parents'),
)
user_type = models.CharField(blank=False, choices=USER_TYPE_CHOICES)
name = models.CharField(blank=False, max_length=255)
country = models.CharField(blank=False, max_length=255)
city = models.CharField(blank=False, max_length=255)
phone = models.CharField(blank=True, max_length=255)
created_at = models.DateField(auto_now_add=True)
def __str__(self):
return self.name
class SchoolAdmin(models.Model):
user = models.OneToOneField(
CustomUser, on_delete=models.CASCADE, primary_key=True)
class Teacher(models.Model):
user = models.OneToOneField(
CustomUser, on_delete=models.CASCADE, primary_key=True)
photo = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True)
class Student(models.Model):
user = models.OneToOneField(
CustomUser, on_delete=models.CASCADE, primary_key=True)
teacher = models.ForeignKey(Teacher)
photo = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True)
class Parent(models.Model):
user = models.OneToOneField(
CustomUser, on_delete=models.CASCADE, primary_key=True)
student= models.ForeignKey(Student)
Your CustomUser class is essentially correct. You don't really need the other classes (SchoolAdmin, Teacher, Student, Parent) as that functionality is described by user_type.
You do need to change the user type choices, something like:
SCHOOL, TEACHER, STUDENT, PARENT = 'school', 'teacher', 'student', 'parent'
USER_TYPES: (
(SCHOOL, 'School'),
(TEACHER, 'Teacher'),
(STUDENT, 'Student'),
(PARENT, 'Parent'),
)
The photo field can be added to the CustomUser class.
Furthermore, you will want to familiarize yourself with Django's documentation on custom authentication and permissions and authorization.
You can then create groups and permissions and assign your users to those. Teachers are a group with specific permissions, etc.
Also you can differentiate by user_type in your templates and views, to show or hide information, or access. For example:
def exam_setup_view(request):
# only teachers allowed here, others see 404
if request.user.user_type != CustomUser.TEACHER:
raise Http404()
EDIT This article explains how to use Django's groups and permissions. For example, you can create custom permissions on your User's Meta class, like so:
class CustomUser(AbstractUser):
# ...
class Meta:
permissions = (
("can_create_exam", "Can create exam"),
("can_view_user", "Can view user"),
)
That will create those custom permissions. You can assign those to users in Django's Admin, or programmatically, as further explained in that article or Django's documentation.
There are several ways of testing if a user has those permissions, ie: use the permission_required decorator, or when you have a user instance in your view: user.has_perm('appname.can_create_exam'), or from a template: {% if perms.appname.can_create_exam %}.
I'm working on a project using Python(3.7) and Django(2.1) in which I need to build a relationship between users and organizations.
I'm using the default Django User model and a profile model to add extra information to users.
Many users can join an organization and an Organization can have many members, a user can create an Organization, these behaviors I need to implement, according to my understanding we need to build a ManyToMany relationship for Organizations model, but don know how to use this relationship to display the information, e.g display a user's organizations on his profile page.
Here are my models:
class Organization(models.Model):
name = models.CharField(max_length=255, blank=False)
users = models.ManyToManyField(User, related_name='members', null=True)
def __str__(self):
return self.name
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='media/default.jpg', upload_to='profile_pics')
goals = MultiSelectField(choices=goals_choices, default='')
def __str__(self):
return f'{self.user.username} Profile'
You can get all organizations of a particular user by:
my_user.members.all()
If you want to access from profile:
my_profile.user.members.all()
But I would suggest to remove related_name or rename it. If you remove it you can access all user organizations as:
my_user.organization_set.all()
my_profile.user.organization_set.all()
For organization you can get all users by:
my_org.users.all()
What I currently have in my models is this:
class Project(models.Model):
project_name = models.CharField(max_length=255, unique=True, blank=False)
def __str__(self):
return str(self.project_name)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
role = models.CharField(choices=ROLE_CHOICES, max_length=255, default='Agent')
Now my question is: Users should be able to have multiple Projects - so I obviously can't use a OneToOne-Field in the Profile-Model.
Later I want to use it for example to just show a user news which are only related to the projects he participates in.
What would be the best strategy to make this possible? Any input is highly appreciated.
Use ManyToMany on project.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
role = models.CharField(choices=ROLE_CHOICES, max_length=255, default='Agent')
project = models.ManyToManyField(Project)
This way one profile can have as many project as he/she wants
On your view you can use this field to filter based on project
I'm using django-allauth and I want to be able to add new field to my User model.
What's the best way to do this as of you ?
I use userena. But I am sure that it will look almost the same ;)
class UserProfile(UserenaBaseProfile):
user = models.OneToOneField(User, unique=True)
city = models.CharField(max_length=32, blank=True, null=True)
in settings.py:
AUTH_PROFILE_MODULE = 'accounts.UserProfile'
See the docs Storing additional information about users, Make a model with OneToOneField relation to User.
from django.contrib.auth.models import User
class UserProfile(models.Model):
# This field is required.
user = models.OneToOneField(User)
# Other fields here
accepted_eula = models.BooleanField()
favorite_animal = models.CharField(max_length=20, default="Dragons.")