I recently started using django rest framework. I have an application in which the user model has a "company" field, so several users can be in the same company and work with common data. Each company can have one or more warehouses in which goods are stored (each company has its own) There is a page in which data is collected from forms in json and sent to the server via api
[
{
"id": 9,
"doc_type": 1,
"warehouse": 5,
"date": "2022-06-07",
"number": 98,
"contragent": 3,
"comment": "",
"items": [
{
"product": 7,
"buy_price": "1689.00",
"sell_price": "2000.00",
"quantity": 1
}
]
},
Problem: If the user somehow gets this api and changes the id for example of the "warehouse" field to the id of the warehouse of another company, then the document will be created anyway, also if the user replaces the id of the "product" field
Question: How can I check the data for compliance with the user's company before creating it?
Here is my code:
#models.py
Class CustomUser(AbstractUser):
...
company = models.ForeignKey(Company, on_delete=models.PROTECT, null=True)
...
class Company(models.Model):
name = models.CharField(max_length=50)
...
class Warehouse(models.Model):
name = models.CharField(max_length=200)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
#serializers.py
class ConsignmentNoteSerializer(serializers.ModelSerializer):
creator = serializers.HiddenField(default=serializers.CurrentUserDefault())
items = ConsignmentItemSerializer(many=True)
class Meta:
model = ConsignmentNote
fields = ['id', 'doc_type', "warehouse", 'date', 'number', 'contragent', 'comment', 'creator', 'items']
read_only_fields = ['id' ]
#This is how I create the document
def create(self, validated_data):
items = validated_data.pop('items')
note = ConsignmentNote.objects.create(**validated_data)
for item in items:
product = item.pop('product')
item = ConsignmentItem.objects.create(consignmentnote=note, product=product ,**item)
return note
I can't figure out where I should declare the create method. In serializers or in views?
#views.py
class ConsignmentNoteViewSet(viewsets.ModelViewSet):
# queryset = ConsignmentNote.objects.all()
serializer_class = ConsignmentNoteSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
user_company = self.request.user.company
return ConsignmentNote.objects.filter(warehouse__company=user_company)
CodePudding user response:
To solve I usually will be creating a custom permission for this api-view. So to use something like
#views.py
class ConsignmentNoteViewSet(viewsets.ModelViewSet):
# queryset = ConsignmentNote.objects.all()
serializer_class = ConsignmentNoteSerializer
permission_classes = [UserIsMemberOfCompany]
def get_queryset(self):
...
To do that, I recommend, create a new file permissions.py inside same app folder. And create your permission class.
#permissions.py
class UserIsMemberOfCompany(permissions.BasePermission):
def has_permission(self, request, view):
# return True if request.user.id if is part of Company
# to do that you could request the user
request.user.id
# request the id of Company by 'supposing that your passing the
# id by somehow in path variable or query_params'.
request.path_variable['company_id']
# and check if the requested user is on this company
# could be a filter or something like
# return False in else
CodePudding user response:
Based on a response from Carlos-Carvalheira
This should work
class UserIsMemberOfCompany(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.user.company.id == obj.warehouse.company.id:
return True
else:
return False