Django permissions for AbstractUser - python

I am trying to hide parts of my API (DRF) from the AbstractUser.
Here is my models.py:
class Company(models.Model):
name = models.TextField()
class User(AbstractUser):
company = models.ForeignKey(Company, null=True)
Here serializers.py:
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (
'username', 'email', 'company', 'first_name', 'last_name', 'date_joined', 'last_login', 'is_active',
'is_staff', 'groups', 'user_permissions'
)
And views.py:
class CompanyList(generics.ListCreateAPIView):
model = Company
serializer_class = CompanySerializer
class UserList(generics.ListCreateAPIView):
model = User
serializer_class = UserSerializer
So what I'm trying to get - get the JSON for the AbstractUser, but he can only see the company he is assigned to.
How can I do that? I have tried using def has_object_permission(), but I'm not really sure how to do it.

You can override the queryset to return user's company only.
class CompanyList(generics.ListCreateAPIView):
model = Company
serializer_class = CompanySerializer
def get_queryset(self):
return Company.objects.filter(pk=self.request.user.company.pk)

Related

Creating view with foreign key model django restframework

I'm fairly new with the django restframework.
I am trying to create a serializer and a view for a model that has a foreignKey.
Models.py
class Job(models.Model):
"""A Job used to create a job posting"""
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
description = models.TextField()
job_type = models.CharField(max_length=12, choices=JOB_TYPE_CHOICES, default='Full-Time')
city = models.CharField(max_length=255)
state = models.CharField(max_length=255)
created_date = models.DateField(auto_now=False, auto_now_add=True)
salary = models.CharField(max_length=255)
position = models.CharField(max_length=255)
employer = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
def __str__(self):
return self.description[:50]
class Applicant(models.Model):
"""A applicant that can apply to a job"""
job = models.ForeignKey(Job, on_delete=models.CASCADE)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
email = models.EmailField(max_length=254)
phone_number = PhoneNumberField()
resume = models.FileField(upload_to=resume_file_path, validators=[validate_file_extension])
def __str__(self):
return self.first_name
The idea is that Applicant will be able to apply to Job.
I can't seem to figure out how to get the Job id when a Applicant is applying to job.
serializers.py
class JobSerializer(serializers.ModelSerializer):
"""Serializer for tag objects"""
class Meta:
model = Job
fields = ('id', 'description', 'job_type', 'city', 'state', 'salary', 'position', 'employer', 'created_date', 'is_active')
read_only_fields = ('id',)
def create(self, validated_data):
"""Create a job posting with user and return it"""
return Job.objects.create(**validated_data)
class ApplyJobSerializer(serializers.ModelSerializer):
"""Serializer for applying to jobs"""
class Meta:
model = Applicant
fields = ('id', 'first_name', 'last_name', 'email', 'phone_number', 'resume')
read_only_fields = ('id',)
views.py
class ApplyJobView(generics.CreateAPIView):
"""Allows applicants to apply for jobs"""
serializer_class = serializers.ApplyJobSerializer
Below is an image of my http://localhost:8000/api/jobPosting/apply/ url
I don't know how i can bring in the Jobs as in option to my view so I can obtain the id field.
I get the following error when POST
null value in column "job_id" violates not-null constraint
You are not passing job field in serializer fields. Try this:
class ApplyJobSerializer(serializers.ModelSerializer):
"""Serializer for applying to jobs"""
class Meta:
model = Applicant
fields = ('id', 'first_name', 'last_name', 'email', 'phone_number', 'resume', 'job')
read_only_fields = ('id',)

Admin: Show related inline ManyToMany objects as MultipleChoiceField

I've overridden the UserAdmin class and wanted to add a user profile and some related objects as inline.
Userprofile works as expected, but for the ManyToMany relations I get now a table with related objects. That's not really ideal for my application, it's a bit cumbersome to change the related objects, and there's no need to add new objects this way.
I'd like to have a simple MultipleChoiceField containing the related objects. Is there an easy way to achieve this?
Here's my userprofile/admin.py:
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy as _
from django_admin_listfilter_dropdown.filters import RelatedOnlyDropdownFilter
from driverslog.models import Branch
from driverslog.models import Car
from .models import Userprofile
User = get_user_model()
class ProfileInline(admin.TabularInline):
model = Userprofile
can_delete = False
max_num = 0
extra = 0
fk_name = 'user'
class CarInline(admin.TabularInline):
model = Car.users.through
can_delete = True
verbose_name = _('Car')
verbose_name_plural = _('Cars')
extra = 0
class BranchInline(admin.TabularInline):
model = Branch.users.through
can_delete = True
verbose_name = _('Branch')
verbose_name_plural = _('Branches')
extra = 0
class CustomUserAdmin(UserAdmin):
inlines = (ProfileInline, BranchInline, CarInline)
list_display = ('username', 'first_name', 'last_name', 'is_staff', 'is_superuser', 'represents')
list_filter = ('is_active', 'is_staff', 'is_superuser', ('groups', RelatedOnlyDropdownFilter),
('branches', RelatedOnlyDropdownFilter), ('profile__represent', RelatedOnlyDropdownFilter),
('car', RelatedOnlyDropdownFilter))
def represents(self, obj):
return obj.profile.represent.count()
represents.short_description = _('Represents')
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
I solved it now without using inline fields.
What I did is overriding UserChangeForm and adding the fields for the reverse relation. It's working as expected and by now I didn't find any drawbacks (until now...).
admin.py:
class CustomUserAdmin(UserAdmin):
inlines = (ProfileInline,)
form = UserForm
...
fieldsets = (
(_('Personal info'), {'fields': ('username', 'first_name', 'last_name', 'email')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
(_('Assignments'), {'fields': ('branches', 'cars')}),
)
forms.py:
class UserForm(UserChangeForm):
branches = forms.ModelMultipleChoiceField(queryset=Branch.objects.all())
cars = forms.ModelMultipleChoiceField(queryset=Car.objects.all())
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
instance = kwargs.get('instance')
if instance:
self.fields["branches"].initial = instance.branches.all().values_list('id', flat=True)
self.fields["cars"].initial = instance.cars.all().values_list('id', flat=True)
def save(self, commit=True):
if self.is_valid():
self.instance.cars.set(self.cleaned_data.get('cars'))
self.instance.branches.set(self.cleaned_data.get('branches'))
return super().save(commit)
models.py:
class Userprofile(models.Model):
class Meta:
verbose_name = _('Profile')
verbose_name_plural = _('Profiles')
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, unique=True, related_name='profile', verbose_name=_('User'))
represent = models.ManyToManyField(get_user_model(), related_name='represent', blank=True, verbose_name=_('Represents'))
After 2017, filter_horizontal attribute added by django.
class CustomUserAdmin(UserAdmin):
filter_horizontal = ('branches', 'cars')

Django admin inline has 2 foreign keys to the same model

Currently i have the transfer model as followed:
class Transfers(models.Model):
class Meta:
db_table = "transfers"
verbose_name = 'Transfer'
verbose_name_plural = 'Transfers'
user = models.ForeignKey("backend.User", null=True, blank=True, related_name='user_transfer', on_delete=models.CASCADE)
to_account = models.ForeignKey("backend.User", null=True, blank=True, related_name='user_transfer_to_account', on_delete=models.SET_NULL)
A transfer object need to have from one user transfer to another user so i need to use the same User model
And in my admin.py:
class TransfersInline(admin.StackedInline):
model = Transfers
can_delete = False
extra = 0
max_num=0
form = TransfersAdminForm
class UserAdminCustom(admin.ModelAdmin):
exclude = ('password', 'last_login', 'is_superuser', 'is_staff', 'groups',
'user_permissions', 'username', 'first_name', 'last_name', 'is_active', 'date_joined')
inlines = [
TransfersInline,
]
def get_queryset(self, request):
qs = super(UserAdminCustom, self).get_queryset(request)
return qs.filter(is_staff=False)
def get_readonly_fields(self, request, obj=None):
return ('id', 'created', 'modified')
admin.site.register(User, UserAdminCustom)
In my admin i would like to display transfers inline that the User existed in user field of Transfers model but inline can't have multiple foreign key to the same model:
<class 'backend.admin.TransfersInline'>: (admin.E202) 'backend.Transfers' has more than one ForeignKey to 'backend.User'
My question is how do i only make TransfersInline use foreign key from user field instead of to_account field ? (transfer inline will have all the transfers object that the User is in user field )
Use fk_name='user' option for TransfersInline class.
You can check the docs

Django REST Framework ModelSerializer read_only_fields not working

I have the following ListCreateAPIView
class TodoAPI(generics.ListCreateAPIView):
permission_classes = (IsAuthenticated, )
serializer_class = TodoSerializer
def get_queryset(self):
user = self.request.user
return Todo.objects.filter(user=user)
And in my serializers.py, I have
class TodoSerializer(serializers.ModelSerializer):
class Meta:
model = Todo
fields = ('id', 'title', 'description',
'completed', 'created_at')
read_only_fields = ('id', )
But the problem is when I POST data into the form, I get the following error:
IntegrityError at /todo/
NOT NULL constraint failed: todo_todo.user_id
models.py
class Todo(models.Model):
user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
title = models.TextField(max_length=50)
description = models.TextField(max_length=200, blank=True, null=True)
completed = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
The problem is not with id field, but with user field. This field is not nullable in DB and since is required. You can just pass current user as defalt, for this use CurrentUserDefault:
class TodoSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Todo
fields = ('id', 'title', 'description',
'completed', 'created_at', 'user')

Django Rest Framework Serialization error in ListAPIView

I am getting below error
Error -- 'many' is an invalid keyword argument for this function"
In ListAPIView while serializing a object.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'uuid', 'email', 'password', 'first_name', 'last_name', 'mobile_no', 'dob', 'username',)
class CorporateProfileSerializer(serializers.ModelSerializer):
user = UserSerializer(many=True)
class Meta:
model = CorporateProfile
fields = ('user', 'id', 'uuid', 'company_name', 'company_type',)
views.py
class CorporateListView(ListAPIView):
serializer_class = CorporateProfile
queryset = CorporateProfile.objects.all()
What am i doing wrong here?
There was my mistake in views. I wrote model in serializer class instead of serializer class.
serializer_class = CorporateProfileSerializer

Categories