I have a model in my app called "portal", in portal/models.py
:
from django.db import models
from django.contrib.auth.models import User
from django.dispatch import receiver
from django.db.models.signals import post_save
from wagtail.snippets.models import register_snippet
from wagtail.admin.edit_handlers import FieldPanel
@register_snippet
class StaffRoles(models.Model):
role = models.CharField(max_length=154, unique=True, help_text="Create new staff roles here, these roles an be assigned to 'staff' users.")
panels = [
FieldPanel('role'),
]
def __str__(self):
return self.role
class Meta:
verbose_name = "Staff Role"
verbose_name_plural = "Staff Roles"
@register_snippet
class Staff(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=1024, blank=True)
roles = models.ManyToManyField(StaffRoles, blank=True)
def __str__(self):
return str(self.user.first_name) + " " + str(self.user.last_name)
class Meta:
verbose_name = "Staff"
verbose_name_plural = "Staff"
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created and instance.is_superuser:
Staff.objects.create(user=instance)
Any new superuser is automatically given the Staff
model. Any existing (non superuser) user can also be added as Staff
.
I want Staff members to be able to set a bio
and a role
. Roles can be added through the Snippets page in Wagtail admin.
Right now, Staff
is registered as a snippet, this means any Staff member can edit another Staff member's Staff attributes.
I want to customise the Wagtail User Account Settings by adding a form to each user's respective Staff attributes. This way, I can lock out the Staff snippet so each user who has a Staff
object linked to their User
model can change only their own Staff
fields/attributes.
Following the Wagtail documentation from here, I first created the file portal/forms.py
with the content:
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.models import User
from wagtail.users.models import UserProfile
from .models import Staff
class CustomStaffSettingsForm(forms.ModelForm):
class Meta:
model = Staff
exclude = []
And another file portal/wagtail_hooks.py
with the contents:
from wagtail.admin.views.account import BaseSettingsPanel
from wagtail.core import hooks
from .forms import CustomStaffSettingsForm
@hooks.register('register_account_settings_panel')
class CustomStaffSettingsPanel(BaseSettingsPanel):
name = 'custom_staff'
title = "Staff settings"
order = 500
form_class = CustomStaffSettingsForm
form_object = 'profile'
This doesn't work as intended. When accessing "Account settings" and scrolling down to the "Staff settings" I can see the CustomStaffSettingsForm
as intended. Although the correct user is pre-selected in the User
field, I can still select other users. Also, the stored values for bio
and roles
aren't being retrieved and new values aren't being saved.
Below are the Staff Roles
registered in the snippets page:
Below is the Staff
model for the user "sid" in the snippets page:
Below is what the Staff settings
section in the "Account settings" looks like.
As you can see, although the User
dropdown is selected to the right user, I can still click and select another user. The bio
and roles
fields aren't filled with the correct values from the snippets page and not shown in the picture, filling and saving values doesn't work.
I hope my question is as detailed as possible, how do I fix this?
After a few hours of digging, I managed to answer my own question.
related_name
needs to be added with the value of profile
to Staff.user. This prevents the 'User' object has no attribute 'profile'
error messageclass Staff(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
...
user
field from the CustomStaffSettingsForm
. This is to prevent users to select a different user and change another user's Staff
fields.class CustomStaffSettingsForm(forms.ModelForm):
class Meta:
model = Staff
exclude = ["user"]
Staff
fields which are stored and also be able to change the stored values. To achieve this, I had to override the get_form
method which CustomStaffSettingsPanel
inherited form Django's BaseSettingsPanel
. It's easier for me to paste the entire wagtail_hooks.py
contents here below.from wagtail.admin.views.account import BaseSettingsPanel
from wagtail.core import hooks
from .forms import CustomStaffSettingsForm
@hooks.register('register_account_settings_panel')
class CustomStaffSettingsPanel(BaseSettingsPanel):
name = 'custom_staff'
title = "Staff settings"
order = 300
form_class = CustomStaffSettingsForm
form_object = 'profile'
def get_form(self):
"""
Returns an initialised form.
"""
kwargs = {
'instance': self.request.user.profile,
'prefix': self.name
}
if self.request.method == 'POST':
return self.form_class(self.request.POST, **kwargs)
else:
return self.form_class(**kwargs)
The key part of the code above is 'instance': self.request.user.profile
. Passing this argument into self.form_class
results in the correct user's stored Staff
fields and values to be retrieved and stored correctly.
This solution allows me to make sure superusers who are also registered as Staff
can edit their own Staff
fields and have access to others, this is a functionality which cannot be achieved easily with Snippets.
As Staff
is no longer registered as a Snippet, we lose access to change another user's Staff
fields. This can be fixed by adding the following in portal/admin.py
:
admin.site.register(Staff)
Only superusers who had been created via python manage.py createsuperuser
or has had staff_status
set to true can access the Django Admin interface at http://localhost:8000/django-admin
. Unlike Snippets, this way can be used to allow only the Django superuser staff to access and modify Staff fields.
What about non-superusers who are registered as Staff
? Non-superusers cannot access the Wagtail admin, therefore they cannot access the Account Settings page. In most cases, this shouldn't be a problem as the ideal use-case is where only the superusers can be set as Staff. If you want to set non-superusers as Staff
you might have to create your own views and forms so the user can change their Staff
fields outside of Wagtail admin.