I have a custom users schema in Django for work with roles or users type, creating an application named userprofile which will be or will setup my custom user model.
In my settings.py I have the following configuration:
INSTALLED_APPS = [
...
'userprofile',
]
#Custom model Users
AUTH_USER_MODEL = 'userprofile.User'
I customize my User class (userprofile/models.py) that inherit of the AbstractUser class for add some fields to my User model due to my requirements demanded me.
I also create these another models for roles/profile users (MedicalProfile, PatientProfile, PhysiotherapistProfile) with their own fields or attributes
In addition MedicalProfile, PatientProfile, PhysiotherapistProfile have a OneToOneField relationship with my custom model/class User so:
from __future__ import unicode_literals
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.dispatch import receiver
from django.db.models.signals import post_save
class User(AbstractUser):
is_medical = models.BooleanField(default=False)
is_physiotherapist = models.BooleanField(default=False)
is_patient = models.BooleanField(default=False)
slug = models.SlugField(max_length=100, blank=True)
photo = models.ImageField(upload_to='avatars', null = True, blank = True)
# Overriding the save method
def save(self, *args, **kwargs):
if self.is_medical:
profile = MedicalProfile(user=self)
super(User, self).save(self, *args, **kwargs)
profile.save()
# We get the profiles user according with their type
def get_medical_profile(self):
medical_profile = None
if hasattr(self, 'medicalprofile'):
medical_profile=self.medicalprofile
return medical_profile
def get_patient_profile(self):
patient_profile = None
if hasattr(self, 'patientprofile'):
patient_profile = self.patientprofile
return patient_profile
def get_physiotherapist_profile(self):
physiotherapist_profile = None
if hasattr(self, 'physiotherapistprofile'):
physiotherapist_profile = self.physiotherapistprofile
return physiotherapist_profile
class Meta:
db_table = 'auth_user'
class MedicalProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
#active = models.BooleanField(default=True)
name = models.CharField(max_length=64)
class PatientProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
#active = models.BooleanField(default=True)
name = models.CharField(max_length=64)
class PhysiotherapistProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
#active = models.BooleanField(default=True)
name = models.CharField(max_length=64)
My Question
I want to focus my question in relation to the override process save() method:
def save(self, *args, **kwargs):
if self.is_medical:
profile = MedicalProfile(user=self)
super(User, self).save(self, *args, **kwargs)
profile.save()
I want, each that an user is created, automatically be created their profile (MedicalProfile, PatientProfile, PhysiotherapistProfile) according to if their field checked (is_medical, is_patient, is_physiotherapist)
The inconvenient that I have is with my override process is the following:
When I create an user via django admin, I get this error
I don't know about it, in relation of the reason by which is setup the user PK to None ...
What alternatives can I have for solve this situation and when I create an user, their profile instance be saved (MedicalProfile, PhysiotherapistProfile, PatientProfile) depending of the attribute checkbo/field (is_medical, is_physiotherapist , is_patient) that I choose?
I bring to all my apologies before, in case of the my question do not be suited or appropriated with the stackoverflow philosophy or by the extense of my question.
The reason that it's extense is that I want give all details for get an answer
Any orientation I will be grateful and will be appreciated
You need to do something in your save method if the user is not medical; you still need to actually save the object.
A fixed implementation would be:
def save(self, *args, **kwargs):
user = super(User, self).save(self, *args, **kwargs)
if self.is_medical:
MedicalProfile(user=self).save()
My class User located in userprofile/models.py is overriding the save method, stayed so:
class User(AbstractUser):
is_medical = models.BooleanField(default=False)
is_physiotherapist = models.BooleanField(default=False)
is_patient = models.BooleanField(default=False)
slug = models.SlugField(max_length=100, blank=True)
photo = models.ImageField(upload_to='avatars', null = True, blank = True)
def save(self, *args, **kwargs):
user = super(User, self).save( *args, **kwargs)
# Creating and user with medical, patient and physiotherapist profiles
if self.is_medical and not MedicalProfile.objects.filter(user=self).exists()\
and self.is_patient and not PatientProfile.objects.filter(user=self).exists()\
and self.is_physiotherapist and not PhysiotherapistProfile.objects.filter(user=self).exists():
medical_profile=MedicalProfile(user=self).save()
patient_profile=PatientProfile(user=self).save()
physiotherapist_profile=PhysiotherapistProfile(user=self).save()
#profile.save()
# Creating and user with medical and patient profiles
elif self.is_medical and not MedicalProfile.objects.filter(user=self).exists()\
and self.is_patient and not PatientProfile.objects.filter(user=self).exists():
medical_profile=MedicalProfile(user=self).save()
patient_profile=PatientProfile(user=self).save()
# Creating and user with medical and physiotherapist profiles
elif self.is_medical and not MedicalProfile.objects.filter(user=self).exists()\
and self.is_physiotherapist and not PhysiotherapistProfile.objects.filter(user=self).exists():
medical_profile=MedicalProfile(user=self).save()
physiotherapist_profile=PhysiotherapistProfile(user=self).save()
# Creating and user with physiotherapist and patient profiles
elif self.is_physiotherapist and not PhysiotherapistProfile.objects.filter(user=self).exists()\
and self.is_patient and not PatientProfile.objects.filter(user=self).exists():
physiotherapist_profile = PhysiotherapistProfile(user=self).save()
patient_profile = PatientProfile(user=self).save()
# Creating and user with medical profile
elif self.is_medical and not MedicalProfile.objects.filter(user=self).exists():
profile = MedicalProfile(user=self)
profile.save()
# Creating and user with patient profile
elif self.is_patient and not PatientProfile.objects.filter(user=self).exists():
profile = PatientProfile(user=self)
profile.save()
# Creating and user with physiotherapist profiles
elif self.is_physiotherapist and not PhysiotherapistProfile.objects.filter(user=self).exists():
profile = PhysiotherapistProfile(user=self)
profile.save()
# We get the profiles user according with their type
def get_medical_profile(self):
medical_profile = None
if hasattr(self, 'medicalprofile'):
medical_profile=self.medicalprofile
return medical_profile
def get_patient_profile(self):
patient_profile = None
if hasattr(self, 'patientprofile'):
patient_profile = self.patientprofile
return patient_profile
def get_physiotherapist_profile(self):
physiotherapist_profile = None
if hasattr(self, 'physiotherapistprofile'):
physiotherapist_profile = self.physiotherapistprofile
return physiotherapist_profile
# We redefine the attributes (create db_table attribute) in class Meta to say to Django
# that users will save in the same table that the Django default user model
# https://github.com/django/django/blob/master/django/contrib/auth/models.py#L343
class Meta:
db_table = 'auth_user'
class MedicalProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
#active = models.BooleanField(default=True)
name = models.CharField(max_length=64)
class PatientProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
#active = models.BooleanField(default=True)
name = models.CharField(max_length=64)
class PhysiotherapistProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
#active = models.BooleanField(default=True)
name = models.CharField(max_length=64)
# Enter the username as slug field
#receiver(post_save, sender = settings.AUTH_USER_MODEL)
def post_save_user(sender, instance, **kwargs):
slug = slugify(instance.username)
User.objects.filter(pk=instance.pk).update(slug=slug)
the save() method let me save the users with all possible combinations of profiles.
But, is there a better way to do this?
Related
I'm having trouble trying to implement (and really map out) a new model object based off a parent model.
The Bucket model below has a category field based off two category_options.
class Bucket(models.Model):
category_options = (
('personal', 'Personal'),
('social', 'Social'),
)
class BucketObjects(models.Manager):
def get_queryset(self):
return super().get_queryset()
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='buckets')
admin_user = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='admin_user')
guest_user = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='guest_user', blank=True)
category = models.CharField(max_length=30, choices=category_options)
...
objects = models.Manager()
bucketobjects = BucketObjects()
class Meta:
ordering = ('-created',)
def save(self, *args, **kwargs):
if self.stock_list:
self.stock_count = len(self.stock_list)
super().save(*args, **kwargs)
else:
super().save(*args, **kwargs)
I would like to create a new object in a completely different model, SocialBucket, when the Bucket model instance is selected as a social category based off the category field above:
class SocialBucket(models.Model):
bucket = models.ForeignKey(Bucket.objects.id, on_delete=models.CASCADE, related_name='social_buckets)
How can I go about populating my SocialBucket model with new objects based off it's parent, Bucket, model?
EDIT: As requested, here is the view I would be using:
view.py
class CreateBucket(generics.CreateAPIView):
permission_classes = [IsAuthenticated]
serializer_class = BucketCreateSerializer
queryset = Bucket.objects.all()
serializer.py
class BucketCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Bucket
fields = ('owner','category','name','about')
read_only_fields = ['owner']
def create(self, validated_data):
user = self.context['request'].user
bucket = Bucket.objects.create(
owner=user,
**validated_data
)
bucket.save()
return bucket
Try changing models.Model, to Bucket:
class SocialBucket(Bucket)
bucket = models.ForeignKey(Bucket.objects.id, on_delete=models.CASCADE, related_name='social_buckets)
I think the best way to do that is to use post_save signal, for example:
from django.db.models.signals import post_save
from django.dispatch import receiver
class Bucket(models.Model):
....
class SocialBucket(models.Model):
bucket = models.ForeignKey(Bucket, on_delete=models.CASCADE, related_name='social_buckets')
#receiver(post_save, sender=Bucket)
def create_social_bucket(sender, instance, created, **kwargs):
if created and instance.category == 'social':
SocialBucket.objects.create(bucket=instance)
Also, there is an alternative way to implement that. It is to override the save method from Bucket model:
class SocialBucket(models.Model):
bucket = models.ForeignKey('Bucket', on_delete=models.CASCADE, related_name='social_buckets') # putting the class of model as a string value to avoid import error
class Bucket(models.Model):
....
def create_social_bucket(self, create_social_bucket=False):
if create_social_bucket:
Social.objects.create(bucket_id=self.id)
def save(self, *args, **kwargs):
if self.stock_list:
self.stock_count = len(self.stock_list)
create_social_bucket = False
if self.pk is None and self.category == 'social':
create_social_bucket = True
super().save(*args, **kwargs)
self.create_social_bucket(create_social_bucket)
Although it is not a advised to do. You can hack and hard code:
class BucketCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Bucket
fields = ('owner','category','name','about')
read_only_fields = ['owner']
def create(self, validated_data):
user = self.context['request'].user
bucket = Bucket.objects.create(
owner=user,
**validated_data
)
bucket.save()
# creating SocialBucket if category is social. You can later update it.
if bucket.category == 'social':
social_bucket = SocialBucket()
social_bucket.bucket = bucket
social_bucket.save()
return bucket
In django 3.0, I have 3 model classes derieved from an abstract model class as:
class AbstractPerson(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField(unique=True)
muser_id = models.IntegerField(null=True, blank=True)
class Meta:
abstract = True
def save(self, *args, **kwargs):
if (not self.pk) and (self.email):
muser = User.objects.get(id=self.muser_id).count()
if muser:
u = User()
u.username = self.first_name.lower()
u.is_staff = True
u.first_name = self.first_name
u.last_name = self.last_name
u.email = self.email
u.save()
return super(AbstractPerson, self).save(*args, **kwargs)
One of the model class: Customer class has been derieved from this abstract class. What I have tried by overriding the save method of the abstract class model to save the instance in django auth user and customer model. The CustomerModelForm is defined as follows This works fine during 'Add customer'.
class CustomerForm(forms.ModelForm):
class Meta:
model = Customer
fields = "__all__"
def clean_email(self):
email = self.cleaned_data.get('email')
if User.objects.filter(email=email).count():
raise forms.ValidationError(_("Email already exists"),
code='invalid')
return email
But during edit Customer, it is possible that the email id of the customer along with all other attributes might also change. How to then access the corresponding auth User details for the same customer? I require to store the User 'id' value in 'muser_id' attribute of the customer instance. I have used instance_post_save method for the same as follows:
def instance_post_save(sender, instance, created, **kwargs):
if created:
#print(type(instance),'iiiii')
try:
u = User()
u.username = instance.first_name
u.is_staff = True
u.first_name = instance.first_name
u.last_name = instance.last_name
u.email = instance.email
u.save(commit=True)
u = User.objects.get(email=instance.email)
instance.update({muser_id: u.id})
instance.save()
print(instance,'tttttttttttttttt')
except Exception as e:
log.info(e)
u = User.objects.get(id=instance.muser_id)
u.username = instance.first_name
u.is_staff = True
u.first_name = instance.first_name
u.last_name = instance.last_name
u.email = instance.email
u.save()
return
This is not allowing me to persist the User id in the customer model instance, so that it can be accessed during Edit customer. How do I accomplish this? Note: The email id is unique across all the models defined in the app including the customer, User etc.
Resolved finally: AbstractPerson class save method changes:
def save(self, *args, **kwargs):
if (not self.pk) and (self.email):
muser = User.objects.filter(email=self.email).count() # changed
The instance_post_save method changes:
def instance_post_save(sender, instance, created, **kwargs):
if created:
muser = User.objects.get(email=instance.email)
instance.muser_id=muser.id
instance.save()
else:
u = User.objects.get(id=instance.muser_id)
u.username = instance.first_name
u.is_staff = True
u.first_name = instance.first_name
u.last_name = instance.last_name
u.email = instance.email
u.save()
return
Try extending the user model:
from django.contrib.auth.models import User
class AbstractPerson(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, editable=False)
some attributes
class OtherPerson(AbstractPerson):
#... attrs ...
Then implement the user registration and configuration of the extended models of the User model, to change fields of some model, for example a child class of AbstractPerson can use a generic view like UpdateView and specify the fields.
So both models would be related to a one-to-one relationship and not by an id that you would have to use constantly to filter and get users.
My following question is about how I can develop a function that I can compare a POST request data (ModelForm) and existing data of model in queryset.
This is mi models.py:
class Employee(models.Model):
dni = models.CharField(max_length=9, null=False, blank=False, default="12345678R")
name = models.CharField(max_length=7)
surname = models.CharField(max_length=8)
email = models.CharField(max_length=20)
telefone_number = models.IntegerField()
user_nick = models.CharField(max_length=10, null=False, blank=False, default="user")
password = models.CharField(max_length=20, null=False, blank=False, default="password")
ticket = models.ManyToManyField(Ticket)
forms.py (EmployerLoginForm only to user_nick and password):
class EmployerForm(forms.ModelForm):
class Meta:
model = Employee
fields = "__all__"
class EmployerLoginForm(forms.ModelForm):
class Meta:
model = Employee
exclude = ['dni', 'name', 'surname', 'email', 'telefone_number', 'ticket']
In this case, to develop login function I am using the EmployerLoginForm in views.py:
_logger = nexus_services_logs.Logging(statics.NEXUS_VIEWS_LOGGING_NAME)
_views_manager_service = nexus_services_views_manager.ViewsManagerService()
_auth = nexus_services_auth.Authentication()
class IndexView(View):
def post(self, request, *args, **kwargs):
form = EmployerLoginForm(request.POST)
if(_auth.check_model_employer_authentication(form, _logger, _views_manager_service)):
if(_views_manager_service.validate_form(form, _logger)):
_views_manager_service.save_form(form, _logger)
return redirect('employerPortal')
else:
return redirect('index')
check_model_employer_authentication(form, _logger, _views_manager_service) is the function where I want compare form data and queryset. I find the problem when I cannot compare the objects using for loop (in auth.py):
class Authentication():
def __init__(self):
self.employer_exist = False
def check_model_employer_authentication(self, model, logger, views_manager_service):
queryset_all_employers = Employee.objects.order_by("id")
context_exployers = views_manager_service.build_context_queryset_employers(queryset_all_employers)
for employer in context_exployers["employers"]:
if(employer.user_nick == model.user_nick and employer.password == model.password):
self.employer_exist = True
logger.info_log("Exist nick with similar password")
return True
else:
logger.error_log("Nick or password not exist or coincidence with object in db")
return False
I have tried using a context but not works.
I am having trouble making a ModelChoiceField queryset in a ModelForm. The related model.objects manager has been overridden to filter the results in order to get only instances created by the actual user. Here are my models :
class Bloc(ModelPrive):
TYPE_BLOC = (
('pleinchamps', 'plein champs'),
('tunnel', 'tunnel'),
('pepiniere', 'pépinière'),
('autre', 'autre'),
)
nb_planche = models.IntegerField(null=True)
caracteristique = models.CharField(max_length=200, null= True, blank=True)
geom = models.PolygonField(srid=4326)
type_bloc = models.CharField(max_length=200, blank=True, choices=TYPE_BLOC)
nom = models.CharField(max_length=200, null=True, unique= True)
class ModelPrive(models.Model):
created_by = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL, editable=False)
class Meta:
abstract = True
objects = ModelPriveManager()
class ModelPriveManager(models.Manager):
def get_queryset(self):
user = get_current_user()
return super().get_queryset().filter(created_by=user)
In my manager the get_current_user() returns the actual user that has been intercepted by a custom middleware.
Here is my form :
class BlocFormList(ModelForm):
choix_bloc = forms.ModelChoiceField(queryset = Bloc.objects.all().order_by('nom'), required=True)
class Meta:
model = Bloc
fields = ['choix_bloc']
Here is my view :
def planification(request):
form_bloc = BlocFormList()
if request.method == 'POST':
# some other code
return render(request, 'planification.html', locals())
The problem is, when I do a Bloc.objects.all() in views I get the expected answer (Bloc.objects.filter(created_by=user)) but when it is done inside the queryset of the modelform, it returns nothing (as if there were no active user).
After some checks, I have found that the model form queryset doesn't even go into the manager.
If someone knows how to correct this, I have no more ideas.
Seeing this post Django ModelForm overriding __init__, I finally found my solution by overriding the init of my ModelForm :
class BlocFormList(ModelForm):
blocs = None
choix_bloc = forms.ModelChoiceField(label='Blocs', queryset=blocs, required=True)
def __init__(self, *args, **kwargs):
self.blocs = Bloc.objects.all()
super(BlocFormList, self).__init__(*args, **kwargs)
self.fields['choix_bloc'].queryset = self.blocs
class Meta:
model = Bloc
fields = ['choix_bloc']
This works fine.
I have a student model which includes a foreign key of django user model, I want to delete the associated user when student is removed (on delete=CASCADE does vice versa), so I override the delete method but after deletion success_url= '/student/' is not working and url still is e.g. '/students/delete/323/' and keeps sending the pk to url, when I don't override delete method it works like a charm. any idea?
class StudentDeleteView(DeleteView):
model = Student
template_name_suffix = '_confirm_delete'
success_url = '/students/'
def delete(self, request, *args, **kwargs):
related_user = Student.objects.get(student_id=kwargs['pk']).user_id
User.objects.filter(id=related_user).delete()
return super(StudentDeleteView, self).delete(request, *args, **kwargs)
class Student(models.Model):
student_id = models.IntegerField()
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='students')
courses = models.ManyToManyField('Course', through='StudentCourse', related_name='students')
classrooms = models.ManyToManyField('Classroom', through='Register', related_name='students')
last_modified_date = models.DateTimeField(null=True)
birth_date = models.CharField(max_length=10, null=True)
image = models.ImageField(upload_to='profile_image', blank=True)