I've two type of users: Students and Institutions.
Both can login in the website and they have different profiles views.
I'd like to use "http://example.com/accounts/" for both types of users but running different logics and displaying different templates for each one.
For example, Students when they go to their profile they can view/modify their attributes as what are they studying, interested courses.. etc. On the other side, the institution users can view/modify attributes of their own model as institution information.
Institution User Type:
class InstitutionProfile(models.Model):
user = models.OneToOneField(User, related_name='client')
gender = models.CharField(max_length=40, choices=GENDERS_TYPES, blank=True)
#Contact Information
location = models.ManyToManyField(Location)
address = models.CharField(max_length=254, blank=True)
zipcode = models.CharField(max_length=56, blank=True)
phone = models.CharField(max_length=56, blank=True)
def __unicode__(self):
return '%s' % format(self.user)
Student User Type:
class StudentProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
about_me = models.TextField(null=True, blank=True)
gender = models.CharField(max_length=40, choices=GENDERS_TYPES, blank=True)
birth = models.DateTimeField(blank=True, null=True)
#Contact Information
location = models.ManyToManyField(Location, related_name='homecountry')
address = models.CharField(max_length=254, blank=True)
zipcode = models.CharField(max_length=56, blank=True)
phone = models.CharField(max_length=56, blank=True)
#Interested
countries_interested = models.ManyToManyField(Location, blank=True, related_name='countries interested')
areas_interested = models.ManyToManyField(StudyArea, blank=True)
levels_interested = models.ManyToManyField(StudyLevel, blank=True)
languages_interested= models.ManyToManyField(LanguageCourse, blank=True)
def __unicode__(self):
return '%s' % format(self.user)
view.py
class InstitutionProfileDetailView(DetailView):
model = get_user_model()
slug_field = "username"
template_name = "account/institution_profile.html"
def get_object(self, queryset=None):
user = super(InstitutionProfileDetailView, self).get_object(queryset)
InstitutionProfile.objects.get_or_create(user=user)
return user
class StudentProfileDetailView(DetailView):
model = get_user_model()
slug_field = "username"
template_name = "account/student_profile.html"
def get_object(self, queryset=None):
user = super(StudentProfileDetailView, self).get_object(queryset)
StudentProfile.objects.get_or_create(user=user)
return user
What is the best solution for having different views with 2 different type of users?
You can use an unified view and return different views from that according to your logic -
def accounts_view(request):
if request.user.is_student(): # <-- check with your logic, is_student() is a stub
return StudentProfileDetailView.as_view()
elif request.user.is_institute():
return InstitutionProfileDetailView.as_view()
And point accounts/ to accounts_view.
Related
i'm trying to filter logbook report based on the logged in industry supervisor,
the supervisor should be able to see the report of students under his supervision
views.py
class LogbookEntryView(ListAPIView):
queryset = LogbookEntry.objects.all()
serializer_class = StudentLogbookEntrySerializer
def get_queryset(self, *args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
request = self.request
user = request.user
if not user.is_authenticated:
LogbookEntry.objects.none()
return qs.filter(student_id__industrysupervisor = request.user.student.industry_based_supervisor)
models.py
LogbookEntry Model
class LogbookEntry(models.Model):
week = models.ForeignKey("api.WeekDates", verbose_name=_("Week Id"), null=True, on_delete=models.SET_NULL)
student = models.ForeignKey("students.Student", verbose_name=_("Student Id"), on_delete=models.CASCADE)
entry_date = models.DateTimeField()
title = models.CharField(_("Title"), max_length=50)
description = models.CharField(_("Description"), max_length=1000)
diagram = models.ImageField(_("Diagram"), upload_to=profile_picture_dir)
Student Model
class Student(models.Model):
user = models.OneToOneField(get_user_model(), null=True, on_delete=models.CASCADE)
profile_pic = models.ImageField(_("profile picture"), upload_to=profile_picture_dir)
department_id = models.ForeignKey(Department, null=True, on_delete=models.SET_NULL)
phone_no = models.CharField(max_length=11)
school_based_supervisor = models.ForeignKey("school_based_supervisor.SchoolSupervisor", verbose_name=_("School Supervisor"), null=True, on_delete=models.SET_NULL)
industry_based_supervisor = models.ForeignKey("industry_based_supervisor.IndustrySupervisor", verbose_name=_("Industry Supervisor"), null=True, on_delete=models.SET_NULL)
placement_location = models.ForeignKey("industry_based_supervisor.PlacementCentre", verbose_name=_("Placement Location"), null=True, blank=True, on_delete=models.SET_NULL)
Industry Supervisor Model
class IndustrySupervisor(models.Model):
user = models.OneToOneField(get_user_model(), null=True, on_delete=models.CASCADE)
profile_pic = models.ImageField(_("profile picture"), upload_to=profile_picture_dir)
phone_no = models.CharField(max_length=11)
placement_center = models.ForeignKey("industry_based_supervisor.PlacementCentre", verbose_name=_("Placement Centre"), null=True, blank=True, on_delete=models.CASCADE)
def __str__(self):
return self.user.username
You can update your get_queryset method in the LogbookEntryView class
def get_queryset(self):
user = self.request.user
# If user is not authenticated, return empty queryset
if not user.is_authenticated:
return LogbookEntry.objects.none()
# If user is not a student, return empty queryset
if not hasattr(user, 'student'):
return LogbookEntry.objects.none()
# Get the industry supervisor for the student
supervisor = user.student.industry_based_supervisor
# If student does not have an industry supervisor, return empty queryset
if supervisor is None:
return LogbookEntry.objects.none()
# Filter logbook entries based on the industry supervisor
queryset = LogbookEntry.objects.filter(student__industrysupervisor=supervisor)
return queryset
I have a form for filling out lessons that I want to limit who the students can select as their teacher to only confirmed connections. I have three models:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=254, unique=True)
name = models.CharField(max_length=254, null=True, blank=True)
class Lesson(models.Model):
user = models.ForeignKey(User, related_name='fencer', on_delete=models.SET_NULL, null=True, blank=True)
teacher = models.ForeignKey(Fencer, related_name='instructor', on_delete=models.SET_NULL, null=True, blank=True)
lesson_date = models.DateField(default="1900-01-01")
title = models.CharField(max_length=100, null = True, blank=True)
description = models.TextField(null=True, blank=True)
class Connection(models.Model):
student = models.ForeignKey(User, related_name='student', on_delete=models.CASCADE, blank=True)
teacher = models.ForeignKey(User, related_name='teacher', on_delete=models.CASCADE, blank=True)
student_accepts = models.BooleanField(default=False)
teacher_accepts = models.BooleanField(default=False)
#property
def connected(self):
if self.student_accepts == True and self.teacher_accepts == True:
return True
else:
return False
My form so far is:
class LessonForm(ModelForm):
class Meta:
model = models.Lesson
#fields = ()
fields = '__all__'
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['teacher'].queryset = Users.objects.filter() # the best I have so far
How do I filter the User model based on the link made in the Connection model? Maybe I'm overcomplicating this or is there a better way?
Thank you in advance
Found the answer in this other question on here about spanning models.
I had a hard time getting django to see the fields for some reason so I will keep this ugly version.
def __init__(self, user, *args, **kwargs): # will need to pass the user to the form when used
super(LessonForm, self).__init__(*args, **kwargs)
# reduce options to just the coaches that the student is connected with
connected_teachers = Connection.objects.filter(Q(student=user) and (Q(student_accepts=True) and Q(teacher_accepts=True)))
teachers = User.objects.filter(teacher__in=connected_teachers)
self.fields['teacher'].queryset = teachers
I have a ledger account table that consist of ledger accounts of all the companies. The user in logged into a specific company and hen he selects an account to use on a form only the accounts that company must be available for the user. for this purpose I use the request.user to determine the user. I however get an error "request does not exist". I understand why it is not available on the forms.py as there is no request executed. Is there a way that I can make request.user available of the form.
Models.py
class tledger_account(models.Model):
id = models.AutoField(primary_key=True)
description = models.CharField(max_length=30, unique=True)
gl_category = models.CharField(max_length=30, choices=category_choices, verbose_name='category', db_index=True)
note = models.CharField(max_length=25, blank=True, default=None)
active = models.BooleanField(default=True)
company = models.ForeignKey(tcompany, on_delete=models.PROTECT, db_index=True)
forms.py
class SelectAccountForm(forms.ModelForm):
date_from = forms.DateField(widget=forms.SelectDateWidget(years=year_range))
date_to = forms.DateField(widget=forms.SelectDateWidget(years=year_range))
select_account = forms.ModelChoiceField(queryset=tledger_account.objects.filter(
company = request.user.current_company))
class Meta:
model = ttemp_selection
fields = ['select_account', 'date_from', 'date_to']
When you use request.user you are using the fields of the user model so it is not necessary to have them in the form, for that you need to have a forensic relationship with the user model:
class tledger_account(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
description = models.CharField(max_length=30, unique=True)
gl_category = models.CharField(max_length=30, choices=category_choices, verbose_name='category', db_index=True)
note = models.CharField(max_length=25, blank=True, default=None)
active = models.BooleanField(default=True)
company = models.ForeignKey(tcompany, on_delete=models.PROTECT, db_index=True)
and the view:
def tledger_account_view(request):
template_name = 'your template'
user = request.user
tledger_account = tledger_account.objects.get(user=user)
return render(request, template_name, {
'tledger_account': tledger_account,
})
more info https://docs.djangoproject.com/en/3.1/topics/auth/default/
I have three user types in user model(create, query and common), multiple type user's authority is messy when I am changing user profile with UpdateView, for example, a user who is admin, when admin clicked a user who is common user, then that user in page is in common user's authority, my UpdateView is as below:
class UserUpdateView(UpdateView):
model = User
form_class = UserForm
context_object_name = 'user'
template_name = 'general/teachers/user_change_form.html'
def get_object(self):
return get_object_or_404(User, pk=self.kwargs['pk'])
models of user:
class User(AbstractUser):
name = models.CharField(max_length=100, verbose_name="姓名", default="", blank=True)
gender = models.CharField(
max_length=7,choices=(("male","男"),("female","女")),
default="female", verbose_name="性别",
)
department = models.ForeignKey(
Department,
on_delete=models.CASCADE, null=True, blank=True, verbose_name="所属部门",
)
job_title = models.CharField(max_length=100, verbose_name="职位", default="", blank=True)
mobile = models.CharField(max_length=11, verbose_name="手机号", default="", blank=True)
email = models.EmailField(max_length=50, verbose_name="邮箱", default="", blank=True)
is_employee = models.BooleanField(default=True, verbose_name='是否是普通用户')
is_teacher = models.BooleanField(default=False, verbose_name='是否是查询用户')
is_supervisor = models.BooleanField(default=False, verbose_name='是否是评测人')
add_time = models.DateTimeField(auto_now=True, verbose_name="添加时间")
forms of user:
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('username', 'department', 'gender', 'job_title', 'email', 'mobile')
I have an app that displays client assets on html posting pages. Each client authorized to use the system is assigned a profile:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
fullname = models.CharField(max_length=64, unique=False)
company = models.CharField(max_length=50, choices=CLIENT_CHOICES)
position = models.CharField(max_length=64, unique=False, blank=True, null=True)
...
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
def __unicode__(self):
return u'%s' % self.fullname
class Meta:
ordering = ['fullname']
class Admin:
pass
and there's a model for the post pages:
class PostPage(models.Model):
client = models.CharField(max_length=50, choices=CLIENT_CHOICES)
job_number = models.CharField(max_length=30, unique=True, blank=False, null=False)
job_name = models.CharField(max_length=64, unique=False, blank=False, null=False)
page_type = models.CharField(max_length=50, default='POST')
create_date = models.DateField(("Date"), default=datetime.date.today)
contact = models.ForeignKey(UserProfile)
contact2 = models.ForeignKey(UserProfile, related_name='+', blank=True, null=True)
contact3 = models.ForeignKey(UserProfile, related_name='+', blank=True, null=True)
contact4 = models.ForeignKey(UserProfile, related_name='+', blank=True, null=True)
def __unicode__ (self):
return u'%s %s %s' % (self.client, self.job_number, self.job_name)
class Admin:
pass
and finally, a very simple view function to display the pages:
def display_postings(request, job_number):
records = PostPage.objects.filter(job_number=job_number)
tpl = 'post_page.html'
return render_to_response(tpl, { 'records': records })
The problem is, if you work for and access the system from "ACME" company, there's no logic in the view that would prevent you from viewing records for "BETAMAX" company in addition to your own. How can I modify my view so that if say, user.profile.company = "ACME" , but the request returns a record where PostPage.client = "BETAMAX", access to the record is denied? Additionally, can I have one company group, say user.profile.company = "MY_COMPANY" that has access to all records?
Write a decorator that checks the company of the request.user for the view. The code would look something like this:
def belongs_to_company(func):
def decorator(request, *args, **kwargs):
has_permissions = False
# get current company
...
# get user's list of company
...
# if company not in user's list of company
if not has_permissions:
url = reverse('no_perms')
return redirect(url)
return func(request, *args, **kwargs)
return decorator
A better long term solution is to check out Role Based Access Control libraries like django-guardian