Django User Profile - default value depend on chosen User - django

I want to create Profile extending User model.
from django.db import models
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
# Create your models here.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
operatorId = models.CharField(
max_length=50,
default= 'O'+str(user.id), #get user id
blank=False,
unique=True,
)
Want to add operatorId which depends on userId chosen (operator id i O and user.id as a string). How to get current user id? basically, I need to change default every-time I change user. Is it possible?

you should override save method
from django.db import models
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
operatorId = models.CharField(
max_length=50,
blank=False,
unique=True,
)
def save(self, *args, **kwargs):
self.operatorId = 'O'+str(self.user.id) # set operatorId here
super().save(*args, **kwargs)
note that overriding save has a limitation:
Unfortunately, there isn’t a workaround when creating or updating
objects in bulk, since none of save(), pre_save, and post_save are
called.
check django docs for more info

Related

Why does Django not find a field added to the AbstractBaseUser

I've inherited from the AbstractBaseUser as follows:
class User(AbstractBaseUser):
"""
Main User model, inherits from AbstractBaseUser
"""
# Meta
email = models.EmailField(verbose_name='email', max_length=60, unique=True)
username = models.CharField(max_length=40, unique=True) # equals to email
date_joined = models.DateTimeField(verbose_name='date joined', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
employee_of = models.OneToOneField(Customer, on_delete=models.SET_NULL, null=True)
So each User is linked to one and only one Customer.
Now within a view I want to access the instance of the current logged in user within the request object and get the employee_of value to get a queryset that contains all users of that customer.
def render_employees(request):
"""
Renders the employees page of the dashboard
:param request:
:return:
"""
# Return the value for the current site for css specific classes
dashboard_site = 'employees'
# Query the employees
qs_employees = User.objects.filter(employee_of=request.user.employee_of) # doesn't find field
...
However the filter doesn't work because request.user.employ_of doesn't seem to return anything. My IDE even suggests e.g. username, date_joined etc. but not employee_of.
Why's that?
class Customer(models.Model):
"""
A table that stores static data about a customer, usually a legal company
"""
legal_name = models.CharField(max_length=50)
street = models.CharField(max_length=30)
street_number = models.CharField(max_length=3)
def __str__(self):
return self.legal_name
Update:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from applications.customer.models import Customer
from django.conf import settings
BaseUser = settings.AUTH_USER_MODEL
class User(AbstractBaseUser):
"""
Main User model, inherits from AbstractBaseUser
"""
# Relations
user = models.OneToOneField(BaseUser, related_name='user_profile', on_delete=models.CASCADE, null=True) # link to default user model
employee_of = models.OneToOneField(Customer, on_delete=models.SET_NULL, null=True, blank=True)
I linked the user to the default user model via Django admin. However in the view im still not able to access employee_of within request.user
It seems that request.user is a different model. It's User model from django.contrib.auth. https://docs.djangoproject.com/en/4.0/ref/contrib/auth/#django.contrib.auth.models.User.
What you can do about it?
In our app we have UserProfile model that have OnetoOne relation to django User.
You can then store employee_of value there.
class UserProfile(AbstractBaseUser):
user = models.OnetoOneField("auth.User", related_name="user_profile", on_delete=models.CASCADE)
employee_of = models.OneToOneField(Customer, on_delete=models.SET_NULL, null=True)
and then access request.user employees using something like
request.user.user_profile.employee_of

Django: 'User' object has no attribute 'perfil_de_usuario'

I'm trying to make a page that, after the user is logued in, shows a list of diferent actions.
The proble is that, when the user is succesfully authenticated, the resulting page is this error:
AttributeError at /iniciar_sesion/
'User' object has no attribute 'perfil_de_usuario'
Request Method: POST
Request URL: http://127.0.0.1:8000/iniciar_sesion/
Django Version: 3.0.3
Exception Type: AttributeError
Exception Value:
'User' object has no attribute 'perfil_de_usuario'
Exception Location: /home/jenifer/Documentos/qbit/mysite4/usuarios/models.py in guardar_usuario_perfil, line 25
The model is as follows:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Perfil_de_Usuario(models.Model):
idusuario = models.AutoField(db_column='idUsuario', primary_key=True)
nombres = models.CharField(max_length=45, blank=True, null=True)
apellidos = models.CharField(max_length=45, blank=True, null=True)
clave = models.CharField(max_length=45, blank=True, null=True)
email = models.CharField(max_length=45, blank=True, null=True)
web = models.URLField(blank=True)
class Meta:
managed = False
db_table = 'Usuario'
#receiver(post_save, sender=User)
def crear_usuario_perfil(sender, instance, created, **kwargs):
if created:
perfil_de_usuario.objects.create(usuario=instance)
#receiver(post_save, sender=User)
def guardar_usuario_perfil(sender, instance, **kwargs):
instance.perfil_de_usuario.save()
For what the error says, the problem is with guardar_usuario_perfil, but I'm not getting how to modify it for this thing to work.
I know there are similar posts and I've tried different solutions like rename instance.perfil_de_usuario.save() part but the result is the same.
If somebody can help me I will apreciate it very much
You should use a OneToOneField so that Django can add a reverse relation on the User model
class Perfil_de_Usuario(models.Model):
idusuario = models.OneToOne(settings.AUTH_USER_MODEL, related_name="perfil_de_usuario")
....
The related_name="perfil_de_usuario" is what Django will add to the User model so that you can do what you were trying to do on the signals.
Also, note I didn't use the user model directly in the OneToOneField but used settings.AUTH_USER_MODEL instead as it is the recommended way.
Here is the Django documentation on extending the user model

How to create multiple users in Django without letting them access each other's data

I am not quite sure if the title I used for my question is the right one. And if this has previously been answered, someone should please point me to it as I have tried so much to find that out but its been futile.
I am currently working on a basic HR system which can be used by different companies. In a much clearer way, how can I create an online system whereby multiple companies can manage their employees online without having access to each other's data? And without even knowing that they use the same system? A perfect example of this kind of system is "Zoho People".
Well you should create your models something like that
if you want them both to log in in your system
from django.db import models
from django.contrib.auth.models import AbstractUser
ROLES =(
('hr', 'HR'),
('employee', 'Employee'),
('master', 'Master')
)
class Company(models.Model):
name = models.CharField(max_length=100, null=False, blank=False)
[...] # your other fields
class User(AbstractUser):
company = models.ForeignKey(on_delete=models.PROTECT, null=False, blank=False)
role = models.CharField(max_length=10, choices=ROLES, blank=False, null=False)
[...] #your other fields
You can assign a role and company to each user.
Then to filter the queryset
User.objects.filter(company=request.user.company)
Another approach if you want only to list/edit the employees of the company, you can create an employee class remove the role from user if you dont need it and add company to the employee class
class Employee(models.Model):
company = models.ForeignKey(Company, on_delete=models.PROTECT, null=False, blank=False)
first_name = models.CharField(max_length=30, blank=False, null=False)
last_name = models.CharField(max_length=30, blank=False, null=False)
[...] #aditional fields
A more organic way to achieve your goal is to use session and define a decorator for your views. For example I want to make sure that a user should not be able to access another user's data. So I defined a decorator that makes this check.
In my use case anonymous sessions are not allowed. To make sure user can access only their dashboard I define a UserSession model and populate UserSession when user logs in.
I defined a decorator which checks if the session exists in Usersession. If it exists, a check is done to ensure user in UserSession and user in the request are same. If they match, the view is called. If not, an error response is sent. I am using JsonResponse. You can send appropriate response as per your use case.
For each view I use this decorator. Create as many decorator as per your use case and decorate your views with them.
For my use case I Defined a UserSession in models.py as follows:
from django.contrib.sessions.models import Session
from django.contrib.auth import get_user_model
class UserSession(models.Model):
user = models.ForeignKey(get_user_model(), on_delete = models.CASCADE)
session = models.ForeignKey(Session, on_delete = models.CASCADE)
I created a decorators.py in my app directory and defined is_user_logged_in method
from functools import wraps
from your_app.models import UserSession
from django.contrib.sessions.models import Session
from django.http import JsonResponse
def is_user_logged_in(function):
#wraps(function)
def wrapper(request, *args, **kwargs):
response_data={}
try:
userSession=UserSession.objects.get(
session__session_key=request.session.session_key
)
except UserSession.DoesNotExist:
response_data['status'] = 'token_invalid'
return JsonResponse(response_data)
if userSession.user == request.user:
return function(request, *args, **kwargs)
else:
response_data['status'] = 'token_user_invalid'
return JsonResponse(response_data)
return wrapper
I decorated my views with is_user_logged_in decorator as follows:
my_decorators = [is_user_logged_in, any_other_decorator]
#method_decorator(my_decorators, name='dispatch')
class ProfileView(View):
def get(self, request):
#Do your thing
def post(self, request):
#Do your thing
References:
Sessions
Decorators
Decorating Class Based Views

Django: Get current username in model field as default value

# models.py
from django.db import models
from django.contrib.auth.models import User
class SiteInfo(models.Model):
site_owner = models.OneToOneField(User, on_delete=models.CASCADE, )
site_url = models.CharField(max_length=40, unique=True, blank=False, default= site_owner, )
How to make "site_url" default value = User.username or site_owner instance?
As far as I know, I don't know exactly how to set a default value the way you want to, but you can override the save method, it does the same thing
from django.db import models
from django.contrib.auth.models import User
class SiteInfo(models.Model):
site_owner = models.OneToOneField(User, on_delete=models.CASCADE, )
site_url = models.CharField(max_length=40, unique=True, blank=False)
def save(self, *args, **kwargs):
if self.pk is None:
self.url = self.user.username
super(SiteInfo,self).save(*args,**kwargs)
You could use a pre_save signal handler.
from django.db.models.signals import pre_save
from django.dispatch import receiver
#receiver(pre_save, sender=SiteInfo)
def default_site_url(sender, instance, **kwargs):
if not instance.site_url:
instance.site_url = instance.site_owner.username
You would need to set blank=True on the site_url model attribute otherwise form validation would never allow a blank site_url to be added in the first place.

Django: User Profile in 1.9

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)