I have the following structure of my project:
I wanted to state the entire business context of my application (at least the one that pertains to the problem I have) to better illustrate the situation. Apologies in advance for the length of the post, that's why.
Two applications called userprofile and rbsessions
project
userprofile
models.py
rbsessions
models.py
In userprofile/models.py I am managing the user schema through the following models: User
, MedicalProfile
, PatientProfile
and TherapistProfile
. Which means that in my system there are users who are doctors, who are patients and who are therapists.
When I create a user via django admin, I must associate one of these attributes:
is_medical: It will be a medical user and if I select it when creating it, a profile of that user will be automatically created in the MedicalProfile model
is_patient: It will be a patient user and if I select it when creating it, a profile of that user will be automatically created in the PatientProfile model
is_therapist: It will be a therapist user and if I select it when creating it, a profile of that user will be automatically created in the TherapistProfile model
It is understood that when a patient user is created in the system, it is because he is going to receive therapy sessions or medical appointments. (I will detail the data of a therapy session later in a class called Session)
When a patient user is created, I want PatientProfile
an instance of the class to be automatically created in addition to his profile (which is already created) Session
(I will detail it later) which will mean that he will have an appointment assigned.
( see last condition elif
in the User model that I will present below )
According to the above, the models User
, MedicalProfile
, PatientProfile
, TherapistProfile
are the following:
User
model(userprofile/models.py
)from __future__ import unicode_literals from django.conf import settings from django.utils import timezone from datetime import datetime 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 from rbsessions.models import Session class User(AbstractUser): birth_date = models.DateTimeField(default=timezone.now()) address = models.CharField(max_length=150, blank=False) phone = models.CharField(verbose_name=u'phone', max_length=25, blank=True) is_medical = models.BooleanField(default=False) is_therapist = models.BooleanField(default=False) is_patient = models.BooleanField(default=False) slug = models.SlugField(max_length=100, blank=True) photo = models.ImageField(upload_to='avatars', blank = False) #Sobreescribo el metodo save para la creacion de cada perfil de usuario def save(self, *args, **kwargs): user = super(User, self).save( *args, **kwargs) # Creating and user with medical, patient and therapist 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_therapist and not TherapistProfile.objects.filter(user=self).exists(): medical_profile=MedicalProfile(user=self).save() patient_profile=PatientProfile(user=self).save() therapist_profile=TherapistProfile(user=self).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 therapist profiles elif self.is_medical and not MedicalProfile.objects.filter(user=self).exists()\ and self.is_therapist and not TherapistProfile.objects.filter(user=self).exists(): medical_profile=MedicalProfile(user=self).save() therapist_profile=TherapistProfile(user=self).save() # Creating and user with physiotherapist and patient profiles elif self.is_therapist and not TherapistProfile.objects.filter(user=self).exists()\ and self.is_patient and not PatientProfile.objects.filter(user=self).exists(): therapist_profile = TherapistProfile(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 ---Here--- elif self.is_patient and not PatientProfile.objects.filter(user=self).exists(): profile = PatientProfile(user=self) profile.save() #Quiero crear una instancia de una sesión de rehabilitacion para cuando se crea un paciente session = Session() session.description = 'This is a rehabilitation session for a patient. This register session ' \ 'will be completed for the medical personal session.status = 'PLA' session.participants = 'By define' session.period = 'By define' session.game_levels = 'By define' session.iterations = 0 session.movements = 'By define' session.games = 'By define' session.medical = MedicalProfile.objects.create(user=self) session.therapist = TherapistProfile.objects.create(user=self) session.patient = PatientProfile.objects.create(user=self) session.save() class Meta: db_table = 'auth_user'
MedicalProfile
model(userprofile/models.py
)class MedicalProfile(models.Model): user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) name = models.CharField(max_length=64) specialty = models.CharField(max_length=64) def __str__(self): return '%s %s' % (self.user.first_name, self.user.last_name)
PatientProfile
model(userprofile/models.py
)class PatientProfile(models.Model): user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) name = models.CharField(max_length=64) blood_type = models.CharField(max_length=4, blank=False) care_provider = models.CharField(max_length=64, blank=False) time_of_evolution = models.CharField(max_length=64, blank=False) affected_limb = models.CharField(max_length=64, blank=False) diagnostic = models.TextField(blank=True) managing_organization = models.CharField(max_length=64, blank=False) def __str__(self): return '%s %s' % (self.user.first_name, self.user.last_name)
TherapistProfile
model(userprofile/models.py
)class TherapistProfile(models.Model): user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) name = models.CharField(max_length=64) specialty = models.CharField(max_length=64) def __str__(self): return '%s %s' % (self.user.first_name, self.user.last_name)
rbsessions application
I mentioned at the beginning that I have another application called rbsessions
from which the therapy or rehabilitation sessions of the patients will be managed, so there is a model called Sessions.
Sessions
model (rbsessions/models.py)from django.db import models from django.utils import timezone # Create your models here. class Session(models.Model): STATUS_PLANNED = 'PLA' STATUS_ARRIVED = 'ARR' STATUS_IN_PROGRESS = 'PRO' STATUS_ON_LEAVE = 'ONL' STATUS_FINISHED = 'FIN' STATUS_CANCELLED = 'CAN' STATUS_CHOICES = ( (STATUS_PLANNED, u'Planned'), (STATUS_ARRIVED, u'Arrived'), (STATUS_IN_PROGRESS, u'In progress'), (STATUS_FINISHED, u'Finished'), (STATUS_CANCELLED, u'Cancelled'), ) description = models.TextField(blank=False) date_session = models.DateTimeField(default=timezone.now()) status = models.CharField(max_length=3, choices=STATUS_CHOICES, default=STATUS_PLANNED) participants = models.TextField(blank=False) period = models.CharField(max_length=25,blank=True) game_levels = models.TextField(blank=True) iterations = models.PositiveIntegerField(blank=True) movements = models.TextField(blank=False) games = models.TextField(blank=False) #slug = models.SlugField(max_length=100, blank=True) #Claves foráneas. medical = models.ForeignKey('userprofile.MedicalProfile') patient = models.ForeignKey('userprofile.PatientProfile') therapist = models.ForeignKey('userprofile.TherapistProfile')
As we can detail, this model has three foreign keys that point to the doctor and physiotherapist users who will be in charge of the session and another to the patient user who will carry it out. My problem that I will state now has to do with this.
When I want to create a patient user, I want them to be automatically assigned a session where they will be treated. In other words, an instance of the class is created for that patient user that was registered Session
.
And this is how in userprofile/models.py
the User class (like this above but I indicate it here again), in this portion of code I ask:
If you are creating a patient user (is_patient checked), create your profile in PatientProfile and immediately create an instance of it Session
with all its corresponding fields or attributes:
The code on how I'm doing it is like this:
class User(AbstractUser):
...
def save(self, *args, **kwargs):
user = super(User, self).save( *args, **kwargs)
# creating users with other profiles
if self.is ...
...
# Creating and user with patient profile
elif self.is_patient and not PatientProfile.objects.filter(user=self).exists():
profile = PatientProfile(user=self)
profile.save()
#Creando la instancia Session que sera la sesión de rehabilitación de ese paciente que se esta creando
session = Session()
session.description = 'This is a rehabilitation session for a patient. This register session ' \
'will be completed for the medical personal'
session.status = 'PLA'
session.participants = 'By define'
session.period = 'By define'
session.game_levels = 'By define'
session.iterations = 0
session.movements = 'By define'
session.games = 'By define'
session.medical = MedicalProfile.objects.create(user=self)
session.therapist = TherapistProfile.objects.create(user=self)
session.patient = PatientProfile.objects.create(user=self)
session.save()
When via Django admin I create a user with the patient profile ( is_patient
checked) I get this.
What IntegrityError
I get is because I am saving an instance of the class Session
within the override of the method save()
that I am performing as well. And in fact before recording Session I call a .save() where I record the patient's profile
So thanks to those save()
successive Django records the same user twice.
I have a .save
inside the def function save()
like this
def save(self, *args, **kwargs): user = super(User, self).save( *args, **kwargs)
# Creating and user with patient profile
if self.is_patient and not PatientProfile.objects.filter(user=self).exists():
profile = PatientProfile(user=self)
profile.save()
#Primer save
session = Session()
session.description = 'This is a rehabilitation session for a patient. This register session ' \
'will be completed for the medical personal'
session.status = 'PLA'
session.participants = 'By define'
session.period = 'By define'
session.game_levels = 'By define'
session.iterations = 0
session.movements = 'By define'
session.games = 'By define'
session.medical = MedicalProfile.objects.create(user=self)
session.therapist = TherapistProfile.objects.create(user=self)
session.patient = PatientProfile.objects.create(user=self)
session.save()
#Segundo save()
I am right?
Something that I wanted to try according to things that had been suggested to me and had also been done before was that instead of recording the instance of Session
a patient user that is created within the override of the method save()
, I used a signal post_save()
to tell Django that when it creates the patient user, create your therapy session.
So I removed the creation of a patient session in the methodsave()
def save(self, *args, **kwargs):
user = super(User, self).save( *args, **kwargs)
# Creating and user with patient profile
if self.is_patient and not PatientProfile.objects.filter(user=self).exists():
profile = PatientProfile(user=self)
profile.save()
And I said the following:
# Signal for save rehabilitation_session of a patient user
@receiver(post_save, sender = settings.AUTH_USER_MODEL)
def creating_rehabilitation_session_patient(sender, instance, **kwargs):
user = instance
#if created:
if user.is_patient:
Session.objects.create(description='This is a rehabilitation session for a patient. This register session'
'will be completed for the medical personal',
status='PLA', participants='By define', period='By define',
game_levels='By define', iterations=0, movements='By define',
games='By define',)
But here it happens to me that I don't know how to tell Django that in Session.objects.create(...)
what I'm doing, I include the values of the attributes medical
, patient
, therapist
( they go after games
in the code immediately above) that are foreign keys of the models MedicalProfile
, PatientProfile
and TherapistProfile
that are related to the modelSession
Apparently with this signal when saving the data, I don't know if it will work, since I also get an IntegrityError, but not because my user already exists in the system (like the previous one), but because since I am not sending values to the fields medical
, patient
, therapist
and these don't accept null values so they can't be empty.
I don't know if the idea of applying the signal post_save()
to my function will def creating_rehabilitation_session_patient()
solve my problem and in which case...
What is the best way to send the parameters of the fields medical
, patient
, therapist
?
Although I don't know if maybe once I can send the fields to it medical
, patient
I therapist
also get the IntegrityError already userprofile exist error correctly (the first one I got), given that finally it is in the override of the save() method or with a signal, I'm finally doing a save... although with the signal it's after... I don't know if my analysis is correct.
How can I achieve this?
UPDATE
I have modified according to the answer I got, the fields session.medical
, session.therapist
, session.patient
in the class Session
inrbsessions/models.py
Now I am calling the functions that return the respective profile of each user, like this:
def save(self, *args, **kwargs):
user = super(User, self).save( *args, **kwargs)
# Creating and user with patient profile
if self.is_patient and not PatientProfile.objects.filter(user=self).exists():
profile = PatientProfile(user=self)
profile.save()
#Primer save
session = Session()
session.description = 'This is a rehabilitation session for a patient. This register session ' \
'will be completed for the medical personal'
session.status = 'PLA'
session.participants = 'By define'
session.period = 'By define'
session.game_levels = 'By define'
session.iterations = 0
session.movements = 'By define'
session.games = 'By define'
#Imrpimo el valor de lo que me retorna get_medical_profile()
print (self.get_medical_profile())
session.medical = self.get_medical_profile()
session.therapist = self.get_therapist_profile()
session.patient = self.get_patient_profile()
session.save()
#Segundo save()
Pero al examinar el contenido de lo que me retorna la función get_medical_profile()
, detallo que le estoy enviando el valor de None
al momento de crear una instancia de la clase Session
[21/Jan/2016 21:40:30] "POST /admin/userprofile/user/add/ HTTP/1.1" 302 0
[21/Jan/2016 21:40:30] "GET /admin/userprofile/user/26/change/ HTTP/1.1" 200 24279
[21/Jan/2016 21:40:30] "GET /admin/jsi18n/ HTTP/1.1" 200 3189
None
Internal Server Error: /admin/userprofile/user/26/change/
Traceback (most recent call last):
File "/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/site-packages/django/core/handlers/base.py", line 149, in get_response
response = self.process_exception_by_middleware(e, request)
File "/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/site-packages/django/core/handlers/base.py", line 147, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/site-packages/django/contrib/admin/options.py", line 541, in wrapper
return self.admin_site.admin_view(view)(*args, **kwargs)
File "/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/site-packages/django/utils/decorators.py", line 149, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/site-packages/django/views/decorators/cache.py", line 57, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/site-packages/django/contrib/admin/sites.py", line 244, in inner
return view(request, *args, **kwargs)
File "/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/site-packages/django/contrib/admin/options.py", line 1438, in change_view
return self.changeform_view(request, object_id, form_url, extra_context)
File "/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/site-packages/django/utils/decorators.py", line 67, in _wrapper
return bound_func(*args, **kwargs)
File "/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/site-packages/django/utils/decorators.py", line 149, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/site-packages/django/utils/decorators.py", line 63, in bound_func
return func.__get__(self, type(self))(*args2, **kwargs2)
File "/usr/lib/python3.4/contextlib.py", line 30, in inner
return func(*args, **kwds)
File "/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/site-packages/django/contrib/admin/options.py", line 1378, in changeform_view
self.save_model(request, new_object, form, not add)
File "/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/site-packages/django/contrib/admin/options.py", line 991, in save_model
obj.save()
File "/home/bgarcial/workspace/neurorehabilitation-system/userprofile/models.py", line 141, in save
session.medical = self.get_medical_profile()
File "/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/site-packages/django/db/models/fields/related_descriptors.py", line 199, in __set__
(instance._meta.object_name, self.field.name)
ValueError: Cannot assign None: "Session.medical" does not allow null values.
Y es normal claro, porque en mis funciones de profiles, inicialmente los profiles respectivos son inicializados a None
# We get the profiles user according with their type
def get_medical_profile(self):
medical_profile = None
#medical_profile = MedicalProfile.objects.get(user=self)
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_therapist_profile(self):
therapist_profile = None
if hasattr(self, 'therapistprofile'):
therapist_profile = self.therapistprofile
return therapist_profile
Creo que al preguntar lo que voy a preguntar ahora tal vez no he acabado de comprender como trabajan las funciones get
de los profiles
, dado que estas han sido inicializadas a None pensaría con el objetivo de que cuando no existan las relaciones OneToOne
no tengamos el error de RelatedObjectDoesNotExist
(¿?)
Intenté cambiar este valor o equivalente por esta linea
medical_profile = MedicalProfile.objects.get(self.user.first_name)
Pero obtuve esto.
AttributeError at /admin/userprofile/user/31/change/
'User' object has no attribute 'user'
Request Method: POST
Request URL: http://localhost:8000/admin/userprofile/user/31/change/
Django Version: 1.9
Exception Type: AttributeError
Exception Value:
'User' object has no attribute 'user'
Exception Location: /home/bgarcial/workspace/neurorehabilitation-system/userprofile/models.py in get_medical_profile, line 158
Python Executable: /home/bgarcial/.virtualenvs/nrb_dev/bin/python
Python Version: 3.4.3
Python Path:
['/home/bgarcial/workspace/neurorehabilitation-system',
'/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4',
'/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/plat-x86_64-linux-gnu',
'/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/lib-dynload',
'/usr/lib/python3.4',
'/usr/lib/python3.4/plat-x86_64-linux-gnu',
'/home/bgarcial/.virtualenvs/nrb_dev/lib/python3.4/site-packages']
Server time: Thu, 21 Jan 2016 22:19:37 +0000
En estos momentos no comprendo como debo manejar el valor retornado de las funciones get_medical_profile, y demás. De pronto puedes explicarme su funcionamiento por favor. Entre otras cosas porque se pregunta por el atributo de esta forma:
if hasattr(self, 'patientprofile'):
Gracias y disculpas
Estoy casi seguro que el problema está en esta parte de tu código:
Estás tratando de crear nuevamente el perfil cuando deberías llamar a las funciones que has creado anteriormente para obtenerlas:
Actualización
Sobre tu duda:
That's right, in relationships with
models.OneToOneField
it is possible that the relationship does not exist, that is why in one of the previous answers I raised it like this. This is so that you can validate it since it is possible that a user does not have all the profiles:The thing is, correct me if I'm wrong, that your model
Session
accepts all three profiles, but in certain cases you may not have all three, so I think your modelSession
should be nullable in those fields:With this change you should no longer have problems using your functions since they return
None
when the user does not have one of the profiles.