I have a list of users or users/roles type. I want to be able to click a link to Update their respective profiles users.
When I perform click in Profile user, I should be able to edit the associated data to respective user according to profile that handle each of them. Each data profile is the simple page or template and manage for the same UpdateView
class based view.
In detail my scenario is the following:
I have the User
(inherit from AbstractBaseUser
) model and here I manage the account data of the all users, this mean all data in common to all users roles or types which I want manage in my application such as:
- user student
- user professor
- user executive
This users/roles type have their own model in where I define the respective fields to each of them. So, I have too, StudentProfile
ProfessorProfile
and ExecutiveProfile
models, of this way:
My User
model is:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
username = models.CharField(max_length=40, unique=True)
slug = models.SlugField(
max_length=100,
blank=True
)
is_student = models.BooleanField(
default=False,
verbose_name='Student',
help_text='Student profile'
)
is_professor = models.BooleanField(
default=False,
verbose_name='Professor',
help_text='Professor profile'
)
is_executive = models.BooleanField(
default=False,
verbose_name='Executive',
help_text='Executive profile',
)
other fields ...
def get_student_profile(self):
student_profile = None
if hasattr(self, 'studentprofile'):
student_profile = self.studentprofile
return student_profile
def get_professor_profile(self):
professor_profile = None
if hasattr(self, 'professorprofile'):
professor_profile = self.professorprofile
return professor_profile
def get_executive_profile(self):
executive_profile = None
if hasattr(self, 'executiveprofile'):
executive_profile = self.executiveprofile
return executive_profile
def save(self, *args, **kwargs):
user = super(User,self).save(*args,**kwargs)
# Creating an user with student profile
if self.is_student and not StudentProfile.objects.filter(user=self).exists():
student_profile = StudentProfile(user = self)
student_slug = self.username
student_profile.slug = student_slug
student_profile.save()
# Creating an user with professor profile
elif self.is_professor and not ProfessorProfile.objects.filter(user=self).exists():
professor_profile = ProfessorProfile(user=self)
professor_slug = self.username
professor_profile.slug = professor_slug
professor_profile.save()
# Creating an user with executive profile
elif self.is_executive and not ExecutiveProfile.objects.filter(user=self).exists():
executive_profile = ExecutiveProfile(user = self)
executive_slug = self.username
executive_profile.slug = executive_slug
executive_profile.save()
# I have this signal to get the username and assign to slug field
@receiver(post_save, sender=User)
def post_save_user(sender, instance, **kwargs):
slug = slugify(instance.username)
User.objects.filter(pk=instance.pk).update(slug=slug)
The idea behind of these schema is that when I create and user with is_student
field checked the StudentProfile
model is used for complete their data.
When I create and user with is_professor
field checked the ProfessorProfile
model is used for complete their data.
When I create and user with is_executive
field checked the ExecutiveProfile
model is used for complete their data.
The models for each Profile (Student
, Professor
and Executive
) are such as follow:
class StudentProfile(models.Model):
user = models.OneToOneField(
User,
on_delete=models.CASCADE
)
slug = models.SlugField(
max_length=100,
blank=True
)
origin_education_school = models.CharField(
_("origin education institute"), max_length=128
)
current_education_school = models.CharField(
_("current education institute"), max_length=128
)
extra_occupation = models.CharField(
_("extra occupation"), max_length=128
)
class ProfessorProfile(models.Model):
CATHEDRAL_PROFESSOR = 'CATHEDRAL'
RESEARCH_PROFESSOR = 'RESEARCH'
INSTITUTIONAL_DIRECTIVE = 'DIRECTIVE'
OCCUPATION_CHOICES = (
(CATHEDRAL_PROFESSOR, 'Cathedral Professor'),
(RESEARCH_PROFESSOR, 'Research Professor'),
(INSTITUTIONAL_DIRECTIVE, 'Institutional Directive'),
)
user = models.OneToOneField(
User,
on_delete=models.CASCADE
)
slug = models.SlugField(
max_length=100,
blank=True
)
occupation = models.CharField(
max_length=255,
blank = False,
)
class ExecutiveProfile(models.Model):
user = models.OneToOneField(
User,
on_delete=models.CASCADE
)
slug = models.SlugField(
max_length=100,
blank=True
)
occupation = models.CharField(
max_length=255,
blank = False,
)
enterprise_name = models.CharField(
max_length=255,
blank = False,
)
I have the form to each profile update data of this way in my forms.py:
class UserUpdateForm(forms.ModelForm):
class Meta:
widgets = {
'gender':forms.RadioSelect,
}
fields = ("username", "email", "is_student",
"is_professor", "is_executive",)
model = get_user_model() #My model User
class StudentProfileForm(forms.ModelForm):
class Meta:
model = StudentProfile
fields = ('origin_education_school', 'current_education_school',
'extra_occupation')
class ProfessorProfileForm(forms.ModelForm):
class Meta:
model = ProfessorProfile
fields = ('occupation',)
class ExecutiveProfileForm(forms.ModelForm):
class Meta:
model = ExecutiveProfile
fields = ('occupation', 'enterprise_name', 'culturals_arthistic',
'ecological')
In my class based view AccountSettingsUpdateView I update the data related to the model Users, this mean the Account Data
class AccountSettingsUpdateView(LoginRequiredMixin, UpdateView):
model = get_user_model()
form_class = forms.UserUpdateForm
# success_url = reverse_lazy('dashboard')
context_object_name = 'preferences'
def get_context_data(self, **kwargs):
context = super(AccountSettingsUpdateView, self).get_context_data(**kwargs)
user = self.request.user
if user.is_student:
profile = user.get_student_profile()
context.update({'userprofile': profile})
elif user.is_professor:
profile = user.get_professor_profile()
context.update({'userprofile': profile})
elif user.is_executive:
profile = user.get_executive_profile()
context.update({'userprofile': profile})
return context
The url for the below view is this
url(r"^preferences/(?P<slug>[\w\-]+)/$",
views.AccountSettingsUpdateView.as_view(),
name='preferences'
),
This view AccountSettingsUpdateView
works OK.
[02/Apr/2017 23:51:17] "GET /accounts/preferences/luisa/ HTTP/1.1" 200 18092
And, in my other view and only in this view I am updating the data related to the profile of each one of these users. this mean the profiles described above:
class AccountProfilesView(LoginRequiredMixin, UpdateView):
# When I ask for user with Student Profile
model = StudentProfile
form_class = forms.StudentProfileForm
# sending the form to ProfessorProfile
second_form_class = forms.ProfessorProfileForm
# sending the form to ExecutiveProfile
third_form_class = forms.ExecutiveProfileForm
success_url = reverse_lazy('dashboard')
template_name = 'accounts/student_form.html'
def get_context_data(self, **kwargs):
context = super(AccountProfilesView, self).get_context_data(**kwargs)
user = self.request.user
if 'form' not in context:
context['form'] = self.form_class(self.request.GET,
instance=user)
if 'form2' not in context:
context['form2'] = self.second_form_class(self.request.GET,
instance=user)
'''
if 'form3' not in context:
context['form3'] = self.third_form_class(self.request.GET,
instance=user)
'''
if user.is_student:
profile = user.get_student_profile()
context.update({'userprofile': profile})
elif user.is_professor:
profile = user.get_professor_profile()
context.update({'userprofile': profile})
elif user.is_executive:
profile = user.get_executive_profile()
context.update({'userprofile': profile})
return context
And the url to this AccountProfilesView
view is this
url(r"^profile/(?P<slug>[\w\-]+)/$",
views.AccountProfilesView.as_view(
model=ProfessorProfile),
name='profile'
),
Note that in the url i am passing the ProfessorProfile
model like parameter, despite that in the view AccountProfilesView
body, I am defining the StudentProfile
model, but what happen is that in the url the model = ProfessorProfile override to the model = StundentProfile which came from the view.
In this moment if I am use the luisa
user with profile StudentProfile
and I going to the url http://localhost:8000/accounts/profile/luisa/
the url is not found:
[03/Apr/2017 01:20:25] "GET /accounts/profile/luisa/ HTTP/1.1" 404 1771
Not Found: /accounts/profile/luisa/
But If I remove the attribute model=ProfessorProfile
that I am passing like parameter in the URL, this mean that my url stay so:
url(r"^profile/(?P<slug>[\w\-]+)/$", views.AccountProfilesView.as_view(), name='profile')
The url http://localhost:8000/accounts/profile/luisa/
is OK
[03/Apr/2017 01:28:47] "GET /accounts/profile/luisa/ HTTP/1.1" 200 4469
This happen because in the view persist the model=StudentProfile attribute.
Until this point, if I am use an user with ProfessorProfile named david and I am going to their profile URL, this is not found
Not Found: /accounts/profile/david/
[03/Apr/2017 01:30:19] "GET /accounts/profile/david/ HTTP/1.1" 404 1769
But I add again the attribute model=ProfessorProfile
that I am passing like parameter in the URL, such as mentioned above, the url of david profile ProfessorProfile
is OK.
[03/Apr/2017 01:33:11] "GET /accounts/profile/david/ HTTP/1.1" 200 4171
The same inconvenient I have with the ExecutiveProfile user type.
According to the previous behavioral, is of this way, which, I am defining the view to ask for the user type roles and render their respective form.
But the inconvenient is that in my view AccountProfilesView
I cannot pass or indicate more than One model.
I am trying indicate one second model in my AccountProfilesView
of this way:
class AccountProfilesView(LoginRequiredMixin, UpdateView):
model = StudentProfile
form_class = forms.StudentProfileForm
second_form_class = forms.ProfessorProfileForm
third_form_class = forms.ExecutiveProfileForm
#success_url = reverse_lazy('dashboard')
template_name = 'accounts/student_form.html'
def get_context_data(self, **kwargs):
context = super(AccountProfilesView, self).get_context_data(**kwargs)
# Indicate one second model
context['professor_profile'] = ProfessorProfile
But the result is the same
In summary my question is:
In the UpdateView class based view ...
How to can I work with multiple model (more precisely three models StudentProfile
, ProfessorProfile
and ExecutiveProfile
) to can render their respective model forms with the order of access to each profile page user?
I want can perform this wuth any amount of ProfileUser that I have.
I unknown if my schema User and ProfileUser model are good of if there is some better alternative of address this challenge.
UPDATE
According to the answer of @Ma0 Collazos their solution works great.
In this moment the goal is can make a combination of the different profiles, and can render the forms of each profile. So, if an user have a is_professor and is_executive profile, can show in their profile view (AccountProfilesView
) their forms respective, this mean that when I go to profile user, can I see the fields the forms professor and the fields the form executive
With the order to get this purpose, I add the scenario in where an user have the combination of profiles in my AccountProfilesView
such as follow:
class AccountProfilesView(LoginRequiredMixin, UpdateView):
# All users can access this view
model = get_user_model()
template_name = 'accounts/profile_form.html'
fields = '__all__'
def get_context_data(self, **kwargs):
context = super(AccountProfilesView, self).get_context_data(**kwargs)
user = self.request.user
if not self.request.POST:
if user.is_student:
profile = user.get_student_profile()
context['userprofile'] = profile
context['form_student'] = forms.StudentProfileForm()
elif user.is_professor:
profile = user.get_professor_profile()
context['userprofile'] = profile
context['form_professor'] = forms.ProfessorProfileForm()
elif user.is_executive:
profile = user.get_executive_profile()
context['userprofile'] = profile
context['form_executive'] = forms.ExecutiveProfileForm()
elif user.is_student and user.is_professor and user.is_executive:
student_profile = user.get_student_profile()
professor_profile = user.get_professor_profile()
executive_profile = user.get_executive_profile()
context['student_profile'] = student_profile
context['professor_profile'] = professor_profile
context['executive_profile'] = executive_profile
context['form_student'] = forms.StudentProfileForm()
context['form_professor'] = forms.ProfessorProfileForm()
context['form_executive'] = forms.ExecutiveProfileForm()
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
user = self.request.user
if user.is_student:
context['form_student'] = forms.StudentProfileForm(self.request.POST)
elif user.is_professor:
context['form_professor'] = forms.ProfessorProfileForm(self.request.POST)
elif user.is_executive:
context['form_executive'] = forms.ExecutiveProfileForm(self.request.POST)
elif user.is_student and user.is_professor and user.is_executive:
context['form_student'] = forms.StudentProfileForm(self.request.POST)
context['form_professor'] = forms.ProfessorProfileForm(self.request.POST)
context['form_executive'] = forms.ExecutiveProfileForm(self.request.POST)
return super(AccountProfilesView, self).post(request, *args, **kwargs)
def form_valid(self, form):
context = self.get_context_data(form=form)
user = self.request.user
user = form.save()
if user.is_student:
student = context['form_student'].save(commit=False)
student.user = user
student.save()
elif user.is_professor:
professor = context['form_professor'].save(commit=False)
professor.user = user
professor.save()
elif user.is_executive:
executive = context['form_executive'].save(commit=False)
executive.user = user
executive.save()
elif user.is_student and user.is_professor and user.is_executive:
student = context['form_student'].save(commit=False)
student.user = user
student.save()
professor = context['form_professor'].save(commit=False)
professor.user = user
professor.save()
executive = context['form_executive'].save(commit=False)
executive.user = user
executive.save()
return super(AccountProfilesView, self).form_valid(form)
And in my profile form template, I have the following small logic in which the render of the form to each profile of a separate way works, but when I ask if an user have the three profiles is_student
, is_professor
and is_executive
such as is code section at the end of my template, and I going to profile page of this user, the three forms does not renderized:
<form method="POST">
{% csrf_token %}
{% if userprofile.user.is_student %}
{% bootstrap_form form_student %}
{% elif userprofile.user.is_professor %}
{% bootstrap_form form_professor %}
{% elif userprofile.user.is_executive %}
{% bootstrap_form form_executive %}
{% elif userprofile.user.is_student and
userprofile.user.is_professor and
userprofile.user.is_executive %}
{% bootstrap_form form_student %}
{% bootstrap_form form_professor %}
{% bootstrap_form form_executive %}
{% endif %}
<input type="submit" value="Save Changes" class="btn btn-default">
</form>
Why my three forms, is not possible show them in one form?