Skip to content

Instantly share code, notes, and snippets.

@emilsas
Last active May 26, 2021 02:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emilsas/0364e1e756759be6017f19cc8fdb41e8 to your computer and use it in GitHub Desktop.
Save emilsas/0364e1e756759be6017f19cc8fdb41e8 to your computer and use it in GitHub Desktop.
DjangoModelPermissions Example
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin
from django.db import models
from django.utils.translation import ugettext_lazy as _
class Account(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True, null=True)
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user can log into this site.'),
)
is_active = models.BooleanField(
_('active'),
default=True,
help_text=_(
'Designates whether this user should be treated as active. '
'Unselect this instead of deleting accounts.'
),
)
USERNAME_FIELD = 'email'
def is_provider(self):
is_prov = False
try:
is_prov = self.provider is not None
except ProviderUserProfile.DoesNotExist:
pass
return is_prov
def is_customer(self):
is_cust = False
try:
is_cust = self.customer is not None
except CustomerUserProfile.DoesNotExist:
pass
return is_cust
class CustomerUserProfile(models.Model):
account = models.OneToOneField(Account, blank=True, null=True, related_name='customer_profile')
product = models.ForeignKey(Product, null=True, related_name='customers', on_delete=models.CASCADE)
name = models.CharField(max_length=100, blank=True)
last_name = models.CharField(max_length=100, blank=True)
phone_number = models.CharField(max_length=100)
class ProviderUserProfile(models.Model):
account = models.OneToOneField(Account, null=True, related_name='provider_profile', on_delete=models.CASCADE)
products = models.ManyToManyField(Product, blank=True, related_name='providers')
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.IntegerField()
from rest_framework import permissions
class OnlyProvidersPermission(permissions.BasePermission):
message = 'Only Providers are allowed to access.'
def has_permission(self, request, view):
return request.user.is_authenticated and (
request.user.is_provider() or request.user.is_superuser)
from rest_framework import viewsets
from rest_framework.permissions import DjangoModelPermissions
from api.permissions import OnlyProvidersPermission
from api.serializers import ProductSerializer
from myapp.models import Product
class ProductViewSet(viewsets.ModelViewSet):
"""
Permissions mut be set in this precise order because DjangoModelPermissions calls
get_queryset and a user that is not an operator will not have one.
"""
serializer_class = ProductSerializer
queryset = Product.objects.none() # Required for DjangoModelPermissions
permission_classes = [DjangoModelPermissions, OnlyProvidersPermission]
# If I invert the order of permission classes it's works perfectly
# permission_classes = [OnlyProvidersPermission, DjangoModelPermissions]
def get_queryset(self):
user = self.request.user
if user.is_superuser:
return Product.objects.all()
# In this line DjangoModelPermissions fails when is tested with a user without provider_profile,
# because ._queryset() always ask for .get_queryset() to determine model name.
return user.provider_profile.products.all()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment