I am working on an intranet web application which needs two types of users. Normal users that can be setup from django admin and specific type of users -
Employees.
I have the following model for Employee type user.
class Employee(models.Model):
emp_name = models.CharField(max_length=500)
slug = models.SlugField(unique=True, default='')
location = models.CharField(max_length=200)
email = models.EmailField()
experience = models.TextField(blank=True)
primary_skill = models.ManyToManyField(PrimarySkill)
secondary_skill = models.ManyToManyField(SecondarySkill)
I tried having a OneToOneField like this as per the official doc and
this article:
user = models.OneToOneField(User, blank=True, null=True, on_delete=models.CASCADE)
#receiver(post_save, sender=User)
def create_employee(sender, instance, created, **kwargs):
if created:
Employee.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_employee(sender, instance, **kwargs):
instance.employee.save()
I realized that this is the opposite of what I want. Every time a User
is created from the admin, there was an entry created in the
app_employee table.
What I want is this:
Every time an Employee is created, I need a User created.
An Employee can be created using a separate signup form, say emp_signup
How do I approach this scenario?
I have achieved this using a custom user based on AbstractUser inspired by this article.
class CustomUser(AbstractUser):
pass
class Employee(CustomUser):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
# other fields
In settings.py, I then add the following key:
AUTH_USER_MODEL = 'myapp.CustomUser'
And wherever I need to refer the User class, I use get_user_model(), which will substitute our custom user, in views and forms as follows:
views.py
from django.contrib.auth import get_user_model
def user_profile(request):
User = get_user_model()
user = get_object_or_404(User, username=request.user.username)
return render(request, 'user/user_profile.html', {
'site_user': user
})
forms.py
class SignUpForm(UserCreationForm):
class Meta:
model = get_user_model()
fields = ('username', 'email', 'password1', 'password2',)
Related
I'm using django-guardian and I encountered some issues with the default mixins. And I want to know if there's a better way to do this.
GitHub Link: https://github.com/iaggocapitanio1/django_homepage
Problem:
If I want to limit access at both the model and object levels, using these two mixins (PermissionRequiredMixin, PermissionListMixin) is not a very easy task. Because the permissions_required attribute is overridden. To get around this I had to create a new attribute "object_permission" and do the following:
Model Looks like:
# Create your models here.
from django.db import models
from localflavor.br import models as localModels
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
class Customer(models.Model):
user: User = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return f'{self.user.first_name} {self.user.last_name}'
class Company(models.Model):
user: User = models.OneToOneField(User, on_delete=models.CASCADE)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='comapnies')
def __str__(self):
return f'{self.user.first_name} {self.user.last_name}'
class Project(models.Model):
name = models.CharField(max_length=100)
owner = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='projects')
class Meta:
permissions = (('read_project', 'Read Project'),)
def __str__(self):
return self.name
class House(models.Model):
rooms = models.IntegerField()
postal_code = localModels.BRPostalCodeField()
project = models.ForeignKey(Project, on_delete=models.CASCADE)
Here I needed to create a new attribute ("object_permission") to limit object-level access
in the View:
class ProjectsListView(PermissionRequiredMixin, PermissionListMixin, ListView):
template_name = 'home/projects.html'
model = models.Project
permission_required = ["homepage.view_project"]
object_permission = ["read_project"]
redirect_field_name = 'next'
login_url = 'login/'
get_objects_for_user_extra_kwargs = {}
def get_object_permission(self, request: HttpRequest = None) -> List[str]:
if isinstance(self.object_permission, str):
perms = [self.object_permission]
elif isinstance(self.object_permission, Iterable):
perms = [p for p in self.object_permission]
else:
raise ImproperlyConfigured("'PermissionRequiredMixin' requires "
"'permission_required' attribute to be set to "
"'<app_label>.<permission codename>' but is set to '%s' instead"
% self.permission_required)
return perms
def get_get_objects_for_user_kwargs(self, queryset):
return dict(user=self.request.user,
perms=self.get_object_permission(self.request),
klass=queryset,
**self.get_objects_for_user_extra_kwargs)
#receiver(post_save, sender=models.Project)
def project_post_save(sender, **kwargs):
"""
Create a Profile instance for all newly created User instances. We only
run on user creation to avoid having to check for existence on each call
to User.save.
"""
project: models.Project = kwargs["instance"]
created: bool = kwargs["created"]
if created:
user = models.User.objects.get(pk=project.owner.user.id)
assign_perm("read_project", user, project)
Am I using the right approach to filter data relative to each user? How do I combine both the page access limitation and the relative data of each user in a class model view?
I am making a movie watching website in which there are users and films and the user model has a ManyToMany Field that references the film model. it's called WatchList and an authenticated user can add any movie they want to this watchlist.
My problem is that I want an API that only gets the ID of a film and adds it to the user's watch list.
these are my models and serializers and I am trying to make a view to implement this API.
# models.py
class Film(models.Model):
filmID = models.AutoField(primary_key=True)
title = models.CharField(max_length=150)
# ...
class User(AbstractBaseUser, PermissionsMixin):
userID = models.AutoField(primary_key=True)
username = models.CharField(max_length=100, unique=True, validators=[RegexValidator(regex="^(?=[a-z0-9._]{5,20}$)(?!.*[_.]{2})[^_.].*[^_.]$")])
email= models.EmailField(max_length=100, unique=True, validators=[EmailValidator()])
name = models.CharField(max_length=100)
watchList = models.ManyToManyField(Film)
objects = UserManager()
USERNAME_FIELD = 'username'
# serializers.py
class WatchListSerializer(serializers.ModelSerializer):
class FilmSerializer(serializers.ModelSerializer):
model = Film
fields = ('filmID', 'title',)
read_only_fields = ('filmID', 'title')
film_set = FilmSerializer(read_only=True, many=True)
class Meta:
model = get_user_model()
fields = ('userID', 'film_set')
read_only_fields = ('userID',)
# views.py
class WatchListAddView(...):
pass
The serializer can be changed. but this kind of shows what I want the api to be. the authentication validation part is already taken care of, so imagine that any request to the view is from an authenticated user.
I would not recommend patching this directly and instead create a separate endpoint for adding removing data to this field.
In your case it would look like this. I show just a small working example, you can adjust it to your needs
from django.shortcuts import get_object_or_404
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
#action(detail=True,
methods=['POST'])
def add_film_to_watch_list(self, request, **kwargs):
film = get_object_or_404(klass=Film, filmID=kwargs.get('filmID'))
user = self.get_object()
user.watchList.add(film)
return Response("Success")
I've an app that deals with multiple user types. I need a way for differentiating them in the admin site. Some code to illustrate.
First I created a User model class that inherits from AbstractUser
class User(AbstractUser):
is_partner = models.BooleanField(default=False)
is_client = models.BooleanField(default=False)
email = models.EmailField(unique=True)
Partners and Clients users has different data:
class ClientProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
...
class PartnerProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
...
Client profile is created through SignUp form I provide, after that, users can update their own profile. In the other hand, Partner profile is created by myself as admin, and I need to do it through django admin site.
So, how do I register two version of the same model? and provide different names for showing in the admin index?
What I did was to change just the queryset in the ModelAdmin class, and register it twice, one for Client and another for Partner but it raises me
django.contrib.admin.sites.AlreadyRegistered
class ClientProfileInline(admin.StackedInline):
model = ClientProfile
can_delete = False
verbose_name_plural = 'Client Profile'
fk_name = 'user'
class ClientUserAdmin(UserAdmin):
inlines = (ClientProfileInline, )
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(is_client=True)#HERE it is the flag for differentiating between Client and Partner
def get_inline_instances(self, request, obj=None):
if not obj:
return list()
return super(ClientUserAdmin, self).get_inline_instances(request, obj)
...as this with PartnerUserAdmin...
admin.site.register(User, PartnerUserAdmin)
admin.site.register(User, ClientUserAdmin)
I resolved it using Proxy models, like this
class PartnerUser(User):
class Meta:
proxy = True
verbose_name = 'Partner'
class ClientUser(User):
class Meta:
proxy = True
verbose_name = 'Client'
admin.site.register(PartnerUser, PartnerUserAdmin)
admin.site.register(ClientUser, ClientUserAdmin)
RELATED: get user profile in django
The above shows how to get user profile but i read that the accepted answer method is deprecated.
How would I create/get/use user profile in django 1.9?
models.py
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
user = models.OneToOneField(User)
address = models.TextField()
......
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
The above code will create a UserProfile record whenever a new user is created on User table. Then you can access the profile details like,
address = request.user.profile.address
get_profile() method returned additional informations about User. Currently, these informations can be stored in Custom User Model or in a seperate model which is related to User Model. You can do that by simply adding one2one relation with User model to your custom User model, or by subclassing the AbstructUserBase model.
Subclassing User Model example:
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
date_of_birth = models.DateField()
...
One2One Relation with User model example:
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.CharField(max_length=100)
I am writing a django web app that models an application where hospital staff and patients can login. The patients, nurses, and doctors all have different permissions and the models need to store different information. I am currently trying to create a user profile model that holds all the common fields, and then create separate models for each type of employee that each have a oneToOneField(UserProfile) attribute. I was wondering how I could tell which type of user was logged in from my views.py file. For example, is it possible to do something like:
if request.user.is_patient():
show patient form
elif request.user.is_doctor:
show doctor form
Here is what I have in models.py so far:
class BaseUser(models.Model):
user = models.OneToOneField(User)
username = models.CharField(max_length=30)
firstName = models.CharField(max_length=50)
middleName = models.CharField(max_length=50)
lastName = models.CharField(max_length=50)
sex = models.CharField(max_length=10)
address = models.CharField(max_length=200)
email = models.CharField(max_length=50)
phone = models.CharField(max_length=10)
User.profile = property(lambda u: BaseUser.objects.get_or_create(user=u)[0])
class PatientUser(models.Model):
user = models.OneToOneField(BaseUser)
existingConditions = models.TextField()
prescriptions = models.TextField()
Well, since you have created a custom BaseUser model, you could set up a set of properties in that class to identify it.
This is a rough example that you could use to test in the view the nature of the user:
class BaseUser(models.Model):
def is_patient(self):
try:
self.patientuser
except PatientUser.DoesNotExist:
return False
else:
return True
def is_doctor(self):
try:
self.doctoruser
except DoctorUser.DoesNotExist:
return False
else:
return True
Then, in your view, you could simply:
if request.user.baseuser.is_doctor():
show doctor form
elif request.user.baseuser.is_patient():
show patient form
To ensure that your users have a baseuser associated, you can take a look at the post-save signal for the User model. You can check how to register actions for these signals here.
Here is a very simple example on how to do this:
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
#receiver(pre_save, sender=User)
def my_handler(sender, **kwargs):
BaseUser.objects.create(user=sender, ...)