I understand the basic user authentication, login, creating accounts, extending user model...
I am trying to create a site where teachers and students can login. Teachers would have access to pages students cannot access with rights to post homeworks ect...
I think it is possible to do this with:
Assigning a user to a specific group upon creation.
Using decorators to limit access to the appropriate group.
.
#login_required
#user_passes_test(not_in_student_group, login_url='/login/')
def some_view(request):
# ...
def not_in_student_group(user):
if user:
return user.groups.filter(name='Student').count() == 0
return False
note I got the above code from:
http://bradmontgomery.blogspot.com/2009/04/restricting-access-by-group-in-django.html
Question:
How does using permission differ from the above approach?
How can permissions be used, and how does defining permission help me achieve the above results?
(If it is possible to do so, should it be used?)
It seems there are a hundred ways that people get to the same results in Django regarding authorization and permissions. Groups are one way, definitely.
Django permissions are usually based on your data, so "table based", or "row based". Row based permissions are not native to Django, you have to either roll your own solution, or use something like django-guardian or django-authority More Here.
The docs on permissions are here
class Quiz(models.Model):
title = models.CharField(max_length=64)
class Meta:
permissions = (
("can_take_quiz", "Can take quiz"),
("can_grade_quiz", "Can Grade Quiz"),
)
With this model, and these permissions, you could see that possibly a student aide would be given permission to grade a particular quiz, or a quiz for a given teacher, this is where row-based permissions would be useful. Implementing something like that (via has_permission) can solve a problem (and is more explicit) than just adding a user to a group.
You can add users to groups like you have already, and then give that entire group permissions to add a quiz, or grade quizes (teachers can add/edit/delete/grade, students can take) quizes, and check based on that.
then your user_passes_test would be user.has_perm('quiz.take_quiz') or instead of a decorator, you could pass the specific quiz to your object based backend.
Related
I am creating an application for teaching management in Wagtail. I create an AdminModal for 'Subjects'. I want to allow only selected user group to access a selected subject. Just like "Page permissions" in 'Add group'. Any idea how to do that?
You can do this by overriding get_queryset method in the ModelAdmin class that is associated with the Subject model.
def get_queryset(self, request):
qs = super().get_queryset(request)
valid_subjects = ['subject1', 'subject2', 'subject3']
return qs.filter(subject_name__in=valid_subjects)
The above example shows a way to restrict users from subject name. If you need to access group, you need to have a relation between Subject model and Group model. Then all you have to do is change the filter query.
You can find more about querying in this documentation.
First, I am assuming when you say AdminModal, you actually mean a ModelAdmin model for subjects. If that is not the case, can you please show me where AdminModal comes from?
The subject will need a field you can use to identify which group should be able to manage it. Then you will need to customize the admin interface for your Subjects model. In particular you will need to enforce some permissions - and create some custom get_queryset methods so users are only shown Subjects they should be able to edit. Start with this part of the documentation and post back when you have more specific questions: https://docs.wagtail.org/en/stable/reference/contrib/modeladmin/primer.html
So let's say i want to authenticate two different user (e.g students with its studentID, and teachers with its teacherID). and both let's say both have different ID pattern.
how should I do that with Custom User ? should I extend from AbsractBaseUser and use BaseUserManager for both of them in the same class or make different class for each students and teacher ?
if so, what should I do with AUTH_USER_MODEL in settings.py ?
can i write it like "AUTH_USER_MODEL = ('accounts.StudentAccount', 'accounts.TeacherAccount' ) ?
I appreciate any of your help and answer !!!
I think you may be over complicating the matter.
To authenticate a user, you need two pieces of information from them - one is public (like their username), the other is private, their password.
Once you have authenticated the user - confirmed they are who they say they are, that is, they have submitted valid credentials, next step is to figure out what they are authorized to do.
There are always two different steps - authentication and then authorization. Authentication confirms who you are, and authorization determines what you are able to do.
Now, in your scenario, students and teachers are logging in. The difference between a student and a teacher (besides having a different id pattern) is that they can do different things on the system.
So the problem is making sure you are giving the right authorization to the user.
The way they authenticate really doesn't matter. In other words, it doesn't matter if students are logging in with their email address and teachers are logging in with their teacherid, in the end - the authentication process is the same.
Now, let us get to Django. By default in Django a user is supposed to enter a username. This can be anything as long as it follows two rules:
Maximum length is 150 characters
It can only contain alphanumeric, _, #, +, . and - characters
Do we need to customize this part? Not really, as both studentid and teacherid will fit within those restrictions.
The next part is how do I differentiate between a "student" and a "teacher"? The easiest way to do this is to extend the user model by adding a custom flag to differentiate between a teacher and a student:
from django.contrib.auth.models import User
class UserType(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
flag = models.IntegerField(choices=((1, 'Student'),(2, 'Teacher')), default=1)
Now, when you create a user - simply set this flag to either 1 or 2, depending on what kind of user you are creating.
Remember, the authentication will not change - just the authorization.
You can then use this flag to further restrict what this user can do. Have a look at the documentation for some examples.
I have a requirement of two types of login in my django project where one login is for students and one login is for teachers.
I have gone through the Django documentation and other internet resources, and I have come up with a simple design solution but I am not sure about the pros and cons as I am still quiet new to Django.
Solution I thought to solve this Problem:
1) For student login, I have succesfully integrated django-allauth and it is working fine.
2) Now for the teacher login, I am thinking to build a model as follows:
class Teacher(models.Model):
teacher = models.OneToOneField(User,unique=True)
identifier = models.CharField(max_length=128)
#other fields
3) Then two forms over this model - Signup and Login for teachers and email verification form: Here I thought that I will create teacher object and student object on successful signup but I will set is_active=False and ask for email verification.On successful verification, I will set is_active=True so that a teacher can successfully login.
4) Avoid students from logging in the teachers section and teachers can login in the students section: Here I though of an identifier field to avoid authenticated students to login in the teachers section.
Please can anyone help me by providing your opinion on this solution or by suggesting some better alternative as I am still reading up more and more Django Documentation on this.
Thanks in Advance!
You could create a single model 'user' and define there permissions by adding them to a group.
And have your Django application for teachers check if there in the group teacher.
You could create separate forms or check based on email (name.student# or name# ) and
before saving the model adding the group.
Keeping is_active on False is alway a good idea if you want to verify that a 'user' has given a correct email.
I did an application which needed different permissions levels for Students, Teachers and a few different other User categories. I'm not sure if it's the best way to do it, but I did it by creating one UserProfile, as Eagllus mentioned, which had several categories for what kind of user profile it was. Something like:
PROFILE_CHOICES = (
('TE', 'Teacher'),
('ST', 'Student'),
)
class UserProfile(models.Model):
user = models.ForeignKey(User)
profile_type = models.CharField(max_length=2, choices=PROFILE_CHOICES)
'''other attributes'''
You can then decorate your views so only users of a certain type can access them.
Its not clear to me what you are trying to do in #3. It sounds like you want the user to be added as a teacher and student but only make them active by email verification?
I'm not familiar with django-auth but django-registration provides user registration where user account are activated via email. This may save you a significant amount of work.
Note: I've since asked this question again given the updates to Django's user model since version 1.5.
I'm rebuilding and making improvements to an already existing Django site and moving it over from Webfaction to Heroku, and from Amazon's SimpleDB to Heroku Postgres (though testing locally on Sqllite3 when developing). A lot of what I'm doing is moving over to use built-in Django functionality, like the Django admin, user authentication, etc.
Conceptually, the site has two kinds of users: Students and Businesses. The two types of users have completely different permissions and information stored about them. This is so much the case that in the original structure of the site, we set up the data model as follows:
Users
ID (primary_key)
Business_or_Student ('B' if business, 'S' if student)
email (unique)
password (hashed, obviously)
...
Students
ID (Foreignkey on Users)
<more information>
...
Businesses
ID (Foreignkey on Users)
<more information>
...
This worked pretty well for us, and we had the bare-bones user information in the Users table, and then any more detailed information in the Students and Businesses tables. Getting a user's full profile required something along this pseudocode:
def get_user_profile(id):
if Users(id=id).Business_or_Student = 'B':
return Businesses(id=id)
else:
return Students(id=id)
In moving over, I've found that Django's built-in User object has pretty limited functionality, and I've had to extend it with a UserProfile class I've created, and then had additional Student and Business tables. Given all of the patching I'm doing with this in the Django admin, and being relatively unfamiliar with Django models since I always did it differently, I'm not sure if this is the best way to go about it, or if I should just stick all of the information for businesses and students in the UserProfile table and just differentiate the two with different groups, or if there's even some way to do this all in the built-in User object.
Since businesses and students also have different interfaces, I'm seriously considering setting up the two as different apps within my Django project, and so separating their views, models, etc. entirely. That would look something like:
MyProject/
MyProject/ (project folder, Django 1.4)
mainsite/
students/
businesses/
One of my biggest concerns is with the Django Admin. In extending User, I already had to add the following code:
class UserProfileInline(admin.StackedInline):
model = UserProfile
can_delete = False
verbose_name_plural = 'profile'
class UserAdmin(UserAdmin):
inlines = (UserProfileInline, )
However, I would like the information for the Business or Student aspects of the user to show up in the Django admin when that User is pulled up, but the ForeignKey part of the model is in the Student and Business model since every Student/Business has a User but every User has only one Student or one Business object connected with it. I'm not sure how to add a conditional Inline for the Admin.
Question: Given this structure and these concerns, what is the best way to set up this site, particularly the data model?
This is not a complete solution, but it will give you an idea of where to start.
Create a UserProfile model in mainsite. This will hold any common attributes for both types of users. Relate it to the User model with a OneToOne(...) field.
Create two more models in each app, (student/business), Business and Student, which have OneToOne relationships each with UserProfile (or inherit from UserProfile). This will hold attributes specific to that type of users. Docs: Multitable inheritance / OneToOne Relationships
You may add a field in UserProfile to distinguish whether it is a business or student's profile.
Then, for content management:
Define the save() functions to automatically check for conflicts (e.g. There is an entry for both Business and Student associated with a UserProfile, or no entries).
Define the __unicode__() representations where necessary.
I hope I understood your problem... maybe this can work? You create a abstract CommonInfo class that is inherited in into the different Sub-classes (student and businesses)
class CommonUser(models.Model):
user = models.OneToOne(User)
<any other common fields>
class Meta:
abstract = True
class Student(CommonUser):
<whatever>
class Business(CommonUser):
<whatever>
In this case the models will be created in the DB with the base class fields in each table. Thus when you are working with Students you run a
students = Students.objects.get.all()
to get all your students including the common information.
Then for each student you do:
for student in students:
print student.user.username
The same goes for Business objects.
To get the student using a user:
student = Student.objects.get(user=id)
The username will be unique thus when creating a new Student or Business it will raise an exception if an existing username is being saved.
Forgot to add the link
I have a couple of different profiles. I want to associate permissions with these profiles. I've done so like this:
class StudentProfile(UserProfile):
school = models.CharField(max_length=30)
class Meta:
permissions = (
("is_student","Can access student pages"),
)
however, when I try and check if that permission exists using has_perm on that profile object, I get an error "'StudentProfile' object has no attribute 'has_perm'" am I not supposed to check for permissions in this way? I've read the docs and that's what I thought I was supposed to do
Edit: After reading the docs again, it seems that has_perm is a method belonging to Users and NOT their profiles. However, when I try to show the permissions:
print user.get_all_permissions()
I get an empty set. Shouldn't I see something like "appname.is_student"
.has_perm is a method on the User object, not on a UserProfile object. If you are trying to validate that a user has the permission has_student, you'd need to do something like this:
user.has_perm('profiles.is_student')
assuming that your StudentProfile model is in a profiles application.
EDIT: To address your rephrased question, you should assign permissions the normal way, either to the group or to a particular user, and use User.has_perm. Your latter example goes completely against the point of the Django permission system.