I have 2 databases. One containing the AUTH that's also extended in the following models.py -
from django.contrib.auth.models import User
class FileIndex(models.Model):
filename = models.CharField(max_length=256)
filetype = models.CharField(max_length=16)
vendorid = models.IntegerField()
vendorname = models.CharField(max_length=256)
tablename = models.CharField(max_length=256)
class Meta:
db_table = 'file_index'
verbose_name = 'File/Vendor Index'
verbose_name_plural = 'File/Vendor Indicies'
def __str__(self):
return self.filename
class UserFile(models.Model):
userid = models.ForeignKey(User)
fileid = models.ForeignKey(FileIndex)
grant_date = models.DateTimeField()
revoke_date = models.DateTimeField(blank=True)
class Meta:
db_table = 'auth_files'
verbose_name = 'User File Matrix'
verbose_name_plural = 'User File Matricies'
the 'tablename' field in FileIndex references a Table Name in another database referenced in a separate App. My current test view I'm using is follows in my views.py
class File_List(generics.ListAPIView):
model = cdx_composites_csv
serializer_class = cdx_compositesSerializer
def get_queryset(self):
"""
This view should return a list of all the purchases for
the user as determined by the username portion of the URL.
"""
filename = self.request.GET.get('filename')
model = get_model('markit', filename)
filedate = self.request.GET.get('filedate')
queryset = model.objects.using('markitdb').filter(Date__contains=filedate)
return queryset
If I'm not logged in it works fine and gives "not authorized" but regardless of if I've set view permissions on the table or not my user can still execute the view.
Model is listed before the function or else it will complain about the model not being there. I want to figure that out later. First I'm trying to understand why my view is still executing even if the user does not have group permission to view the Model.
I've attacked this by doing the following in my view -
class ExampleView(APIView):
model = cdx_composites_csv
serializer_class = cdx_compositesSerializer
def get(self, request, format=None):
if UserFile.objects.filter(fileid_id=1, userid_id=2).exists():
content = {
'status': 'Request Successful.'
}
return Response(content)
else:
content = {
'status': 'Request Failed.'
}
return Response(content)
Essentially after authentication it's doing a query against the UserFile to validate the User and the file exists and if it does then I can write it to do the queryset or not.
Related
I setup everything for implementing elastic search in Django rest framework. I launched the elastic server using bat file and it runs successfully in the background. Then when I call the api , it throws me above error.
class Category(models.Model):
name = models.CharField(max_length=255)
name = models.CharField(max_length=255)
description = models.TextField(blank=True)
status = models.BooleanField(default=True)
My search.py
class PaginatedElasticSearchAPIView(APIView, LimitOffsetPagination):
serializer_class = None
document_class = None
#abc.abstractmethod
def generate_q_expression(self, query):
"""This method should be overridden
and return a Q() expression."""
def get(self, request, query):
try:
q = self.generate_q_expression(query)
search = self.document_class.search().query(q)
response = search.execute()
print(f'Found {response.hits.total.value} hit(s) for query: "{query}"')
results = self.paginate_queryset(response, request, view=self)
serializer = self.serializer_class(results, many=True)
return self.get_paginated_response(serializer.data)
except Exception as e:
return HttpResponse(e, status=500)
class CategorySearchView(PaginatedElasticSearchAPIView):
serializer_class = CategorySerializers
document_class = CategoryDocument
def generate_q_expression(self, query):
return Q(
"multi_match",
query=query,
fields=[
"name",
],
fuzziness="auto",
)
My serializers. py
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Subject
fields = ["id", "name","description","status"]
My documents.py:
#registry.register_document
class CategoryDocument(Document):
# name = TextField()
class Index:
name = "category"
settings = {"number_of_shards": 1, "number_of_replicas": 0}
class Django:
model = Category
fields = [
"name","description"
]
My urls.py :
http://localhost:8000/elastic/search/Math/
I tried to call this api family but it shows the above error. Is this because I have some models which has a foreign key fields associated with the Category model?
I want to build a REST API where user can do operations on objects, based on their permissions. Consider a record that represents a car - it contains the license number, the type of the car and extra information. Also consider the following user system:
Owners - Who own the car object. Can modify it and delete it.
Editors - Who can only modify the object properties.
Viewers - Can only view the object properties.
Each record can contain multi owners/editors/viewers (The user who created the object should be automatically the owner). Also, owners can add or remove editors/viewers. In my head, I see it as a list of owners/editors/viewers.
So in case of a GET request, I want to be able to return all objects that the user has permissions for, separated into those three categories.
So under my api app, I have the following code:
The models.py file contains:
class CarRecord(models.Model):
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
The serializers.py file contains:
class CarRecordSerializer(serializers.ModelSerializer):
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
class Meta:
model = CarRecord
fields = ('__all__')
In view.py I have:
class CarRecordViews(APIView):
def get(self, request):
if not request.user.is_authenticated:
user = authenticate(username=request.data.username, password=request.data.password)
if user is not None:
return Response(data={"error": "invalid username/password"}, status=status.HTTP_401_UNAUTHORIZED)
# return all records of cars that user some type of permission for
Now, I want to get all the records of user that he has permissions to query (along with their permission type). I thought of adding a three extra fields under CarRecord - each one is a list of users that contains that permission type. But I'm not sure if it's the "Django way". So wanted to consult first with SO.
EDIT: I tried to add the following field to my CarRecord class:
owners = models.ManyToManyField(User, related_name='car_owners', verbose_name=('owners'), default=[])
Also I added:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username']
lass CarRecordSerializer(serializers.ModelSerializer):
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
owners = UserSerializer(many=True)
class Meta:
model = CarRecord
fields = ('__all__')
And the way I create the CarRecordSerializer instance is:
serializer = CarRecordSerializer(data=request.data)
But I get:
{
"error": {
"owners": [
"This field is required."
]
}
}
How to make it work? I guess is my problem is how to serialize a ManyToMany object?
EDIT2: My second attempt is:
class CarRecord(models.Model):
date_created = models.DateTimeField()
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
owners = models.ManyToManyField(User, related_name='car_owners', verbose_name=('owners'), default=[]))
creator = models.ForeignKey(User,on_delete=models.DO_NOTHING)
# ...
class CarRecordSerializer(serializers.ModelSerializer):
date_created = serializers.DateTimeField(default=datetime.now(timezone.utc))
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
creator = serializers.StringRelatedField()
owners = serializers.PrimaryKeyRelatedField(many=True,read_only=True)
class Meta:
model = CarRecord
fields = '__all__'
def create(self, validated_data):
self.owners = [self.context['creator']]
record = CarRecord(**validated_data, creator=self.context['creator'])
record.save()
return record
# ...
# In post method:
serializer = CarRecordSerializer(data=request.data, context={ 'creator': user })
But now, in GET method, I filter the owners list with the user and it can't find the objects:
> CarRecord.objects.filter(owners=user)
<QuerySet []>
Also, in the Admin section I see that all of the objects automatically have all the users in the owners/editors/viewers lists. Why is that? Owners should contain only the user that created the record and editors and viewers should be empty lists. In another query, owner can add additional owners/editors/viewers.
Here is the solution I might think is the right one
class CarRecord(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
owners = models.ManyToManyField(User, related_name='car_owners')
creator = models.ForeignKey(User,on_delete=models.DO_NOTHING)
class CarRecordSerializer(serializers.ModelSerializer):
creator = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), required=False)
owners_details = UserSerializer(source='owners', many=True, read_only=True)
class Meta:
model = CarRecord
fields = '__all__'
def create(self, validated_data):
try:
new_owners = validated_data.pop('owners')
except:
new_owners = None
car_record = super().create(validated_data)
if new_owners:
for new_owner in new_owners:
car_record.owners.add(new_owner)
return car_record
In views.py
from rest_frameword import generics
from rest_framework import permissions
class CustomCarRecordPermissions(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method == 'GET':
return True
elif request.method == 'PUT' or request.method == 'PATCH':
return request.user == obj.creator or request.user in obj.owners.all()
elif request.method == 'DELETE':
return request.user == obj.creator
return False
class CarRecordListCreate(generics.ListCreateAPIView):
permission_classes = (IsAuthenticated, )
serializer_class = CarRecordSerializer
queryset = CarRecord.objects.all()
def post(self, request, *args, **kwargs):
request.data['creator'] = request.user.id
return super().create(request, *args, **kwargs)
class CarRecordDetailView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = (CustomCarRecordPermissions, )
serializer_class = CarRecordSerializer
lookup_field = 'pk'
queryset = CarRecord.objects.all()
models is self explanatory;
In CarRecord serializers we set creator as required False and primary key related field so that we can supply request user id before create as shown in views.py post method.
In Detail view we set our custom permission; If the request is GET we allow permissions. But if the request is PUT or PATCH the owners and the creator are allowed. But if it is a delete request only creator is allowed.
I think the django-rest-framework-guardian package fits here. This package is based on django-guardian.
django-guardian is an implementation of object permissions for Django providing an extra authentication backend.
There is no change on your models.py
You should change serializers.py and views.py.
For example, your serializer should look like this
from rest_framework_guardian.serializers import ObjectPermissionsAssignmentMixin
class CarRecordSerializer(ObjectPermissionsAssignmentMixin, serializers.ModelSerializer):
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
class Meta:
model = CarRecord
fields = ('__all__')
def get_permissions_map(self, created):
current_user = self.context['request'].user
readers = Group.objects.get(name='readers')
editors = Group.objects.get(name='editors')
owners = Group.objects.get(name='owners')
return {
'view_car_record': [current_user, readers, owners],
'change_car_record': [current_user, editors],
'delete_car_record': [current_user, owners]
}
and your views should look like this:
from rest_framework_guardian import filters
class CarRecordModelViewSet(ModelViewSet):
queryset = CarRecord.objects.all()
serializer_class = CarRecordSerializer
filter_backends = [filters.ObjectPermissionsFilter]
Edit settings.py like this:
INSTALLED_APPS = [
'rest_framework',
'guardian',
]
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
"guardian.backends.ObjectPermissionBackend",
]
You can define filter backends globally in your settings, too:
REST_FRAMEWORK = {
"DEFAULT_FILTER_BACKENDS": [
"django_filters.rest_framework.DjangoFilterBackend",
"rest_framework_guardian.filters.ObjectPermissionsFilter",
],
}
Don't forget! If you define the ObjectPermissionsFilter in the settings.py, your all views are affected by this filter.
If you want to restrict post request per user, you shoul implement custom permission class, like this:
from rest_framework import permissions
class CustomObjectPermissions(permissions.DjangoObjectPermissions):
"""
Similar to `DjangoObjectPermissions`, but adding 'view' permissions.
"""
perms_map = {
'GET': ['%(app_label)s.view_%(model_name)s'],
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
'HEAD': ['%(app_label)s.view_%(model_name)s'],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
Check this link to get the detailed information for the CustomObjectPermissions
You can write permission class car owner user.
Your model.
class CarRecord(models.Model):
date_created = models.DateTimeField()
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
owners = models.ManyToManyField(User, related_name='car_owners', verbose_name=('owners'), default=[]))
creator = models.ForeignKey(User,on_delete=models.DO_NOTHING)
Permission class permission.py
from rest_framework.permissions import BasePermission,
from cars.models import CarRecord
class isCarAccess(BaseCommand):
def has_permission(self, request, view):
if request.method == 'OPTIONS':
return True
check_user = CarRecord.objects.filter(owners__in=[request.user])
return request.user is not None and request.user.is_authenticated and check_user
this permission class will check that does user exists, user is authenticated and as well the user belongs to the card record or not.
And you can pass this permission in your view.
from .permission import isCarAccess
from .models import CarRecord
class CarRecordViews(APIView):
permission_classes = [isCarAccess]
def get(self, request):
car_record = CarRecord.objects.filter(owners__in=[request.user])
# return all records of cars that user some type of permission for
and your settings.py
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"oauth2_provider.contrib.rest_framework.OAuth2Authentication",
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.TokenAuthentication",
),
}
I am trying to update my data in 'VoterList' model by using PUT api, but i don't know which function should i use in my 'views.py' file to handle the coming PUT request because in PUT api, we use parameters from URL to pick the relevent entry from model for updation and then update it by using data received from PUT api.
model.py
class VoterList(models.Model):
# id = models.IntegerField(auto_created= True, primary_key=True)
name = models.CharField( max_length=20)
email = models.EmailField()
mobile = models.IntegerField()
city = models.CharField( max_length=20)
type = models.CharField(max_length=20)
def __str__(self):
return self.name
serializers.py
class FillVoterListSerializers(serializers.HyperlinkedModelSerializer):
class Meta:
model = VoterList
fields = ('id','name', 'email', 'mobile', 'city', 'type')
def update(self, instance, validated_data):
instance.name = validated_data.pop("name", instance.name)
instance.email = validated_data.pop("email", instance.email)
instance.save()
return instance
I will manage the code for PUT in serializers by myself.
views.py
class UpdateVoter(APIView):
serializer_class = FillVoterListSerializers
permission_classes = (AllowAny,)
def post(self, request,*args,**kwargs):
isDataExist = VoterList.objects.get(id=request.data.get('id'))
if not isDataExist:
return Response({"message":"No Voter exist with this id."})
else:
isDataUpdated = self.serializer_class(isDataExist, request.data, partial=True)
if isDataUpdated.is_valid():
isDataUpdated.save()
return Response({"message": "Voter updated."})
else:
return Response({"message": "All fields are Mandatory."})
urls.py
urlpatterns = [
url('api/updateVoter/(?P<id>[0-9]+)/$', UpdateVoter.as_view(), name= "updateVoter")]
So what code should i write in my view.py to handle the PUT request.
Note: I want to tell you that i am preparing api for mobile applications, so please respond accordingly.
Any help is appreciated.
You can use the put() function in your view similar to the post() which you've used
def put(self, request, pk, format=None):
# Your code here
Refer the DRF docs : https://www.django-rest-framework.org/tutorial/3-class-based-views/
I'm working a project, and use Django REST framework and mongo engine, and I'm confused a question two days, and the detail see below:
class Jvv(EmbeddedDocument):
unit = fields.StringField()
unitValue = fields.IntField()
class Meta:
db_table = 'imagerecognition'
class ImageRecognition(Document):
imageUrl = fields.StringField(default='', max_length=100)
createTime = fields.DateTimeField(default=datetime.now())
ddPercent = fields.FloatField(required=False, default='')
jvv = fields.ListField(fields.EmbeddedDocumentField(Jvv))
def __str__(self):
return self.imageUrl
class Meta:
db_table = 'imagerecognition'
then the serializer.p document is :
class JvvSerializer(mongoserializers.EmbeddedDocumentSerializer):
class Meta:
model = Jvv
fields = '__all__'
class ImageUrlSerializer(mongoserializers.DocumentSerializer):
jvv = JvvSerializer(many=True)
class Meta:
model = ImageRecognition
fields = ('imageUrl', 'createTime', 'ddPercent', 'jvv')
and the views.py content is below:
class ImageUrlSave(views.APIView):
def get(self, request, *args, **kwargs):
imgs = ImageRecognition.objects(imageUrl='白菜')
serializer = ImageUrlSerializer(imgs, many=True)
ImageRecognition(imageUrl='土豆', ddPercent=8.22, jvv={'unit':'m', 'unitValue':12}).save()
data = serializer.data
return Response({
'msg': 'SUCCESS',
'code_status': 1000,
'result': data
})
the question is the mongodatabases have been completed, I want to take some data from it, but when I runserver, it shows
raise ValueError("The source SON object needs to be of type 'dict'")
ValueError: The source SON object needs to be of type 'dict', how can I handle this problem, and I am Looking forward to get answer. Thank you.
I am trying to have a logged in user add Media to their media field in the users profile in a DRF generic create view. Has anyone tried this? Here's my view and model:
class MediaCreate(generics.CreateAPIView):
"""
To create a media object, send a post request to:
/profiles/media/create/
In the format:
Audio: "audio file upload"
Title: "char field"
"""
queryset = Media.objects.all()
serializer_class = MediaSerializer
class Musician(ProfileModel):
summary = models.TextField(blank=True)
company = models.CharField(max_length=60, blank=True)
media = models.ManyToManyField('Media', blank=True)
timestamp = models.DateTimeField(auto_now_add=True, blank=True)
def __str__(self):
return '{}'.format(self.user.username)
So I decided to do this with a function api view instead so I can add the model to the profile using the request and not requiring any get request
I know the check if method == Post is redundant but it doesn't harm anything:
#api_view(['POST'])
def MediaCreate(request):
context = {}
logged_on = False
if request.user.is_authenticated():
logged_on = True
visitor = request.user.musician
serializer = MediaSerializer(data=request.data)
if request.method == "POST":
if serializer.is_valid():
serializer.save()
try:
x = serializer.instance
visitor.media.add(x)
context['upload'] = True
except:
error = "Media Not Added to Profile"
context['logged_on',
'error',
'upload'] = logged_on, error, False
return JsonResponse(
data=context,
status=status.HTTP_400_BAD_REQUEST)
context['logged_on'] = logged_on
return JsonResponse(data=context, status=status.HTTP_200_OK)
all you need to do define in your serializers.py the two classes:
class MediaSerializer(serializers.ModelSerializer):
class Meta:
model = Media
class MusicianSerializer(serializers.ModelSerializer):
media = MediaSerializer(many=True, read_only=true)
class Meta:
model = Musician
In order to update the media field for specific musician you send only the media_id and not the whole object as show the following example:
data = {
"summary": "text summary here"
"company": "Company A"
"media_id": 3
}
in your views.py you should define the following view and override get_object to fit your needs:
class MusicianUpdatingApiView(generics.UpdateAPIView):
"""
To add a media to a user, send a post request to:
/profiles/:id/media
"""
serializer_class = MusicianSerializer
def get_object(self):
summary = self.request.data.get("summary")
company = self.request.data.get("company")
media_id = self.request.data.get("media_id")
musician_id = self.kwargs['id'] # cause it send in the url
data = {
"summary": summary
"company": company
"media_id": media_id
}
updated_musician, created = Musician.objects.update_or_create(
id=musician_id,
defaults=data
)
return updated_musician
in urls.py
url(r'^profiles/(?P<id>\d+)/media$', MusicianUpdatingApiView.as_view()),