I have two classes Organisation, and Staff in addition to the User class. I want to create a mixin called "UserIsAdminMixin" that checks whether the user logged in has the role "admin" in a specific Organisation.
The classes (simplified)
class Organisation(models.Model):
name = models.CharField(max_length=50)
class Staff(models.Model):
class Role(models.TextChoices):
ADMIN = 'ADMIN', "Admin"
STAFF = 'STAFF', "Staff"
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, db_index=True, related_name="staff_profiles", on_delete=models.SET_NULL)
organisation = models.ForeignKey('organisations.Organisation', db_index=True, related_name="staff", on_delete=models.CASCADE)
role = models.CharField(max_length=10, choices=Role.choices, default=Role.STAFF)
I then currently have this for my UserIsAdmin mixin:
class UserIsAdminMixin:
def dispatch(self, request, *args, **kwargs):
if self.request.user.staff_profiles.filter(organisation=self.get_object(), role=Staff.Role.ADMIN):
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
This works great for this view:
organisations.views.py (URL: organisation/<int:pk>)
class OrganisationDetail(LoginRequiredMixin, UserIsAdminMixin, generic.DetailView):
model = Organisation
template_name= "organisations/detail.html"
login_url = "login"
But I'd also like it to work for this view as well, which it obviously doesn't as self.get_object() returns a Staff object in this case when it's expecting an Organisation object:
staff.views.py (URL: organisation/<int:pk_alt>/staff/<int:pk>)
class StaffDetail(LoginRequiredMixin, UserIsAdminMixin, generic.DetailView):
model = Staff
template_name="staff/detail.html"
login_url = "login"
I was able to make changes to the mixin to get it to work in the second scenario but not the first:
class UserIsAdminMixin:
def dispatch(self, request, *args, **kwargs):
if self.request.user.staff_profiles.filter(organisation__pk=self.kwargs['pk_alt']), role=Staff.Role.ADMIN):
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
So is there any way I can change the mixin so it works for both Organisation and Staff models to check that the User is a staff member with the role of admin for a given organisation?
Any help would be greatly appreciated!
Many Thanks,
GoingRoundInCircles
Well, a possible solution is to have a get_organisation_id() method in both your views.
Your mixin:
class UserIsAdminMixin:
def dispatch(self, request, *args, **kwargs):
organisation_id = self.get_organisation_id()
if self.request.user.staff_profiles.filter(organisation_pk=organisation_id, role=Staff.Role.ADMIN):
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
StaffDetail:
class StaffDetail(LoginRequiredMixin, UserIsAdminMixin, generic.DetailView):
model = Staff
template_name="staff/detail.html"
login_url = "login"
def get_organisation_id(self):
return self.kwargs.get('pk_alt')
OrganisationDetail:
class OrganisationDetail(LoginRequiredMixin, UserIsAdminMixin, generic.DetailView):
model = Organisation
template_name= "organisations/detail.html"
login_url = "login"
def get_organisation_id(self):
return self.get_object().id
Related
I need to filter in EmployeeForm just to show me options of Companies related to user is logged in. I'm using ModelForm and CreateViews
This is my code:
models.py:
class Company(models.Model):
reg_user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
cuit = models.CharField(max_length=20)
class Employee(models.Model):
company = models.ForeignKey(Empresa, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
cuil = models.CharField(max_length=20)
forms.py:
class EmployeeForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
...
self.fields['name'].widget.attrs['autofocus'] = True
What is the code I need to write in the ellipses? It's currently showing me all companies even ones not owned by the user.
I use forms.Form for this instead of ModelForm, but what i do is:
class EmployeeForm(forms.Form):
companies = forms.ModelChoiceField(queryset=None)
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['companies'].queryset = Company.objects.filter(reg_user=user)
I finally could solve this by using this in views.py
def get_form(self):
form = super(EmployeeCreateView, self).get_form()
form.fields['company'].queryset = Company.objects.filter(reg_user=self.request.user)
return form
Thanks for everyone!
I want to get the current user logged in my forms.py class for show his biography in a CharField (with placeholder or value)
i've tried the init method but it doesn't work for me idk what i'm doing wrong ...
here is my form class
class ProfileSettingsForm(forms.Form):
avatar = forms.ImageField(required=False)
description = forms.CharField(required=False, max_length=230, widget=forms.Textarea(attrs={"value": "here i want the description of the current user"}))
with init
class ProfileSettingsForm(forms.Form):
def __init__(self, user, *args, **kwargs):
self.user = user
super(ProfileSettingsForm, self).__init__(*args, **kwargs)
avatar = forms.ImageField(required=False)
description = forms.CharField(required=False, max_length=230, widget=forms.Textarea(attrs={"value": self.user}))
I tried this method
class ProfileSettingsForm(forms.Form):
user = None
def __init__(self, user, *args, **kwargs):
self.user = user
super(ProfileSettingsForm, self).__init__(*args, **kwargs)
avatar = forms.ImageField(required=False)
description = forms.CharField(required=False, max_length=230, widget=forms.Textarea(attrs={"value": self.user}))
but it return None
in my views.py:
form = forms.ProfileSettingsForm(user=request.user)
If you're using Class-Based Views you can override the get_form_kwargs method.
views.py
# View might not be the correct class for you to inherit from, it's more a example. Having said that, you'd probably use CreateView
class YourView(View):
def get_form_kwargs(self):
form = super().get_form_kwargs()
form['user'] = self.request.user
return form
forms.py
class ProfileSettingsForm(forms.Form):
def __init__(self, user, *args, **kwargs):
self.user = user
super(ProfileSettingsForm, self).__init__(*args, **kwargs)
avatar = forms.ImageField(required=False)
description = forms.CharField(required=False, max_length=230, widget=forms.Textarea(attrs={"value": self.user}))
I've been working on my Django Project.
I use custom Forms (a class called Form), and I've got a class called Restricted_Form. The point of this class is that an Admin user ("staff status" user") has the option restrict which users or groups can have access to the Forms/submit filled Forms:
#....models.py:
from django.contrib.auth.models import User, Group
class Restricted_Form(models.Model):
Form = models.ForeignKey('forms.Form')
Authorized_Users = models.ManyToManyField(User, blank=True)
Authorized_Groups = models.ManyToManyField(Group, blank=True)
user = models.ForeignKey(User, blank=True, null=True, related_name="restriction_owner")
"Form" itself has:
user = models.ForeignKey(User, blank=True, null=True)
#this is always the user who created it
My issue is limiting what the non-superuser admins can do. They should only be able to create Restricted_Form objects including a Form they have created themselves. In practice, when they create a Restricted_Form, only "Forms" that they have created themselves should appear as options to select, in the drop-down menu.
This is my related admin class right now:
#...admin.py
class RestrictedFormAdmin(admin.ModelAdmin):
fieldsets = [
(None, {"fields": ("Form", "Authorized_Users", "Authorized_Groups")}),]
def save_model(self, request, obj, form, change):
if getattr(obj, 'user', None) is None:
obj.user = request.user
obj.save()
def get_queryset(self, request):
qs = super(RestrictedFormAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(user=request.user)
def has_change_permission(self, request, obj=None):
if not obj:
return True
return obj.user == request.user or request.user.is_superuser
admin.site.register(Restricted_Form, RestrictedFormAdmin)
Help will be greatly appreciated.
You can try something like this:
class RestrictedFormAdmin(admin.ModelAdmin):
def render_change_form(self, request, context, *args, **kwargs):
context['adminform'].form.fields['Form'].queryset = Form.objects.filter(user=request.user)
return super(RestrictedFormAdmin, self).render_change_form(request, context, args, kwargs)
I am trying to log the activities during save operation to track all the changes to user model. my approach is as follows.
class User(AbstractUser):
undergrad_college = models.CharField(max_length=20, choices=COLLEGE_CHOICES)
undergrad_degree = models.CharField(max_length=20, choices=COLLEGE_DEGREES)
postgrad_college = models.CharField(max_length=20, choices=COLLEGE_CHOICES)
postgrad_degree = models.CharField(max_length=20, choices=COLLEGE_DEGREES)
currently_working_on = models.TextField()
previous_work_experience = models.TextField()
previous_internship_experience = models.TextField()
def __str__(self):
return self.username
def save(self, *args, **kwargs):
Log(user=User, actions="Updated profile",
extra={"undergrad_college": self.undergrad_college,
"undergrad_degree": self.undergrad_degree,
"postgrad_college": self.postgrad_college,
"postgrad_degree": self.postgrad_degree,
"currently_working_on": self.currently_working_on,
"previous_work_experience": self.previous_work_experience,
"previous_internship_experience": self.previous_internship_experience
})
super(User, self).save(args, **kwargs)
my views are like this for handling the logging.
class ActivityMixin(LoginRequiredMixin):
def get_context_data(self, **kwargs):
context = super(ActivityMixin, self).get_context_data(**kwargs)
context['activities'] = Log.objects.filter(user=self.request.user)
return context
class IndexListView(ActivityMixin, ListView):
template_name = 'pages/home.html'
model = User
I get this error while performing the update action.
Cannot assign "<class 'users.models.User'>": "Log.user" must be a "User" instance.
Update view is as follows
class UserUpdateView(LoginRequiredMixin, UpdateView):
form_class = UserForm
# we already imported User in the view code above, remember?
model = User
# send the user back to their own page after a successful update
def get_success_url(self):
return reverse("users:detail",
kwargs={"username": self.request.user.username})
def get_object(self, **kwargs):
# Only get the User record for the user making the request
return User.objects.get(username=self.request.user.username)
How to assign the User model instance to the Log function. I cant get this working. I am Django newbie.
Looks like pretty straightforward, replace User with self:
Log(user=User, ...
Log(user=self, ...
I am using Django REST Framework to create an API for my web app. I have a class 'Comment', that has depth=2 set in the Meta class. This works great when GETing the Comments. When I try to send a POST or PUT request though (i.e. create a new Comment) I am told I need to include objects instead of ForeignKey IDs.
Here's my Serializer class:
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
depth = 2
The model:
class Comment(models.Model):
user = models.ForeignKey(User, null=True, blank=True,
related_name='comments')
budget = models.ForeignKey(Budget, related_name='comments')
published = models.BooleanField(default=False)
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
The view code:
class Comments(generics.ListCreateAPIView):
model = Comment
serializer_class = CommentSerializer
def pre_save(self, obj):
obj.user = self.request.user
And the error that is displayed in the output (JSON) is:
{"user": ["This field is required."], "budget": [{"non_field_errors": ["Invalid data"]}]}
When this raw data is sent:
{"budget": 2, "published": true, "body": "Another comment"}
I know this is a little bit late but I ended up using 2 serializers like so:
class CommentReadSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
depth = 2
class CommentWriteSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
Then used like this:
class Comments(generics.ListCreateAPIView):
model = Comment
serializer_class = CommentReadSerializer
def create(self, request, *args, **kwargs):
serializer = CommentWriteSerializer(data=request.DATA, files=request.FILES)
if serializer.is_valid():
self.pre_save(serializer.object)
self.object = serializer.save(force_insert=True)
self.post_save(self.object, created=True)
headers = self.get_success_headers(serializer.data)
serializer = CommentReadSerializer(serializer.object)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
You can set different serializers by overriding the get_serializer_class() function, like so:
def get_serializer_class(self):
method = self.request.method
if method == 'PUT' or method == 'POST':
return YourWriteSerializer
else:
return YourReadSerializer
I thought to add this one, since i came here from Googling after a while.
I believe the proper way to define a serializer field that refers to a foreign key relationship is through something like serializers.PrimaryKeyRelatedField. I don't believe that model serializers automatically use this field class without defining it explicitly in the serializer class.
http://www.django-rest-framework.org/api-guide/relations/#primarykeyrelatedfield
I would imagine that a PrimaryKeyRelatedField serializer would correctly handle JSON data submissions like the one you used in your example.
I had the same problem so I Solved making custom generic methods.This is better implementation of above answers
class CustomListCreateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
"""
Concrete view for listing a queryset or creating a model instance.
"""
def get_serializer_class(self):
method = self.request.method
if method == 'PUT' or method == 'POST':
return self.writeSerializers
else:
return self.readSerializers
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
Similarily RUD,
class CustomRetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
"""
Concrete view for retrieving, updating or deleting a model instance.
"""
def get_serializer_class(self):
method = self.request.method
if method == 'PUT' or method == 'POST':
return self.writeSerializers
else:
return self.readSerializers
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs) # enter code here
Now I just give writeSerializers and readSerializers values in Views.py
Also to create Read-write Serializers there is an easy way.
class employeeWriteSerializer(serializers.ModelSerializer):
class Meta:
model = employee
fields = ('username','email',..)
class employeeReadSerializer(serializers.ModelSerializer):
class Meta(employeeWriteSerializer.Meta):
depth = 1
It saves time and repetitive work you can also add authentication classes in custom generic Api(Retitve work). Thanks.