Django Tastypie add Content-Length header - python

I am quite new in Django development. I have a Resource and Model:
Model
class Player(models.Model):
pseudo = models.CharField(max_length=32, unique=True)
ModelResource
class PlayerResource(ModelResource):
class Meta:
queryset = Player.objects.all()
resource_name = 'player'
authentication = BasicAuthentication()
authorization = Authorization()
serializer = Serializer(formats=['xml'])
filtering = {
'pseudo': ALL,
}
And I am requesting all the players with /api/v1/player/?format=xml but it seems that the response header : Content-Length is missing which causes some issues in my app. How can I add it in the response header ?
Thanks a lot.

The lack of Content-Length was due to the lack of a Middleware.
For more information :
Look here : How do I get the content length of a Django response object?
But you can manually add the Content-Length like this :
def create_response(self, request, data, response_class=HttpResponse, **response_kwargs):
desired_format = self.determine_format(request)
serialized = self.serialize(request, data, desired_format)
response = response_class(content=serialized, content_type=build_content_type(desired_format), **response_kwargs)
response['Content-Length'] = len(response.content)
return response

You can add Content-Length header by overriding create_reponse method in your own resource for ex:
class MyResource(ModelResource):
class Meta:
queryset=MyModel.objects.all()
def create_response(self, ...)
# Here goes regular code that do the method from tastypie
return response_class(content=serialized, content_type=build_content_type(desired_format), Content-Length=value, **response_kwargs)

Related

AssertionError: Cannot apply DjangoModelPermissionsOrAnonReadOnly

I have a problem with (I think) permissions. When I try to send post request via postman, I get following error:
AssertionError: Cannot apply DjangoModelPermissionsOrAnonReadOnly on a view that does not set .queryset or have a .get_queryset() method.
[29/Nov/2021 20:21:21] "POST /api/blog/category HTTP/1.1" 500 101581
Here are my views.py and category class. Also, I noticed that for some reason in def post(self, request, format=None) request is not being called ("request" is not accessed Pylance). I think the problem is somewhere here, but can't figure it out. Thank you in advance.
class BlogPostCategoryView(APIView):
serializer_class = BlogPostSerializer
permissions_classes = (permissions.AllowAny, )
def post(self, request, format=None):
data = self.request.data
category = data['category']
queryset = BlogPost.objects.order_by('-date_created').filter(category__iexact=category)
serializer = BlogPostSerializer(queryset, many=True)
return Response(serializer.data)
In settings.py you can add:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
]
}
Then in postman go to the Authorization tab and for type select Basic Auth.
You can then enter your username and password. You should be able to make Post requests after this.

How to add suffix url in Django Rest Framework?

How to add suffix url in ModelViewSet
Serializer
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
def update(self, instance, validated_data):
...
...
ModelViewSet
I'm doing a custom partial update
class CommentViewSet(viewsets.ModelViewSet):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
http_method_names = ['get', 'patch', 'head', 'options']
def partial_update(self, request, *args, **kwargs):
super(CommentViewSet, self).partial_update(
request, *args, **kwargs)
return Response({
"data": request.data,
...
...
})
Urls
router = routers.DefaultRouter()
router.register(
"comments",
CommentViewSet
)
urlpatterns = [
path('api/v1/', include(router.urls))
]
Currently have this, but I want to add a suffix
url: http://localhost:8000/api/v1/comments/{id}
I want to do something like this
url: http://localhost:8000/api/v1/comments/{id}/update_or_whatever
What you want to do does not follow the REST architecture and popular practice. In REST, each endpoint represents a resource. The actions on the resource are represented by HTTP methods. So if you have the comments resource accessible through this url http://localhost:8000/api/v1/comments/, you can create (POST), get the list (GET) on the list endpoint and edit(PUT and PATCH), fetch a single comment (GET) and delete(DELETE) using the detail endpoint. In this way, you don't need to explicitly name the URL according to the action like http://localhost:8000/api/v1/comments/{id}/update. This is the architecture that DRF is built on and hence why you have this url style. Of course, there are actions like login and others that may not fit into this architecture and that's why DRF provides custom actions. But you should not use it to override the default actions mapped to HTTP methods
Another magic from DFR
https://www.django-rest-framework.org/api-guide/viewsets/#viewset-actions
Only change what u need in the view and add this action decorator.
In your views.py
#action(methods=['get'], detail=True, permission_classes=[IsAuthenticated])
def get_file(self, request, pk=None):
if pk is None:
raise ValueError("Found empty filename")
obj = self.get_queryset().filter(pk=pk).first()
if obj and obj.image_file:
return FileResponse(obj.image_file, content_type="image/jpeg")
return Response(
'Nothing to show',
status=status.HTTP_400_BAD_REQUEST)

Object of type User is not JSON serializable

I am creating a an auto complete search for my web page and where I am trying to fetch name of users from database using ajax calls.
My AJAX call is running fine and going to designated URL.
I have tried using JSON encoder but that also did not work.
I am a bit new to DJANGO.Please help
My views.py
def autocomplete(request):
if request.is_ajax():
q = request.GET.get('search', '').capitalize()
search_qs = Profile.objects.filter(user__username__startswith=q)
results = []
print (q)
for r in search_qs:
results.append(r.user)
data= json.dumps(list(results), cls=DjangoJSONEncoder)
else:
data = 'fail'
mimetype = 'application/json'
return HttpResponse(data, mimetype)
My models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
def __str__(self):
return f'{self.user.username} Profile'
Error I am getting
TypeError: Object of type User is not JSON serializable
[] "GET /ajax_calls/search/?**term=he** HTTP/1.1" 500 15860
I don't know why you are querying through Profile, where you can query directly through User. I think the proper implementation should be like this:
from django.core.serializers import serialize
users = User.objects.filter(username__startswith=q)
str_data = serialize('json', users, cls=DjangoJSONEncoder). # Or you don't need to provide the `cls` here because by default cls is DjangoJSONEncoder
data = json.loads(str_data)
Documentation can be found here.

Django-Filter returns the request object

I was trying to dynamically filter the queryset in Django using Django-filters library in DRF but the filters send the response exactly as the same to request ie if i choose mode = transfer the response is
{
"data": {
"mode": "transfer"
}
}
the filterset_class is defined in the views and queryset is passed on to it along with the request type.
views.py
from .filters import DataFilter
def get(self, request):
query_set = ModelName.objects.all()
queryset_filter = DataFilter(
request.GET, queryset=query_set)
return Response({"data": queryset_filter.data}, status=status.HTTP_200_OK)
filters.py
from django_filters import rest_framework as filters
from .models import ModelName
from common.models import MODE_CHOICES
class DataFilter(filters.FilterSet):
currency = filters.ModelMultipleChoiceFilter(lookup_expr='iexact')
coin_type = filters.ModelMultipleChoiceFilter(lookup_expr='iexact')
mode = filters.ModelChoiceFilter(
lookup_expr='iexact', choices=MODE_CHOICES)
max_amount = filters.NumberFilter(
field_name='price', lookup_expr='gt')
min_amount = filters.NumberFilter(
field_name='amount', lookup_expr='lt')
class Meta:
model = ModelName
fields = ('currency', 'coin_type', 'mode',
'max_amount', 'min_amount')
The django-filters documentation suggests django-filter backend can be used by default by adding it to the DEFAULT_FILTER_BACKENDS.
Is there some configuration that i am missing or something
The filterset.data attribute is literally the data/query params you provide to DataFilter on initialization. In this case, you are providing request.GET as the data.
What you want to access is the filterset's .qs property, which is the filtered results. e.g.,
def get(self, request):
queryset_filter = DataFilter(request.GET, queryset=ModelName.objects.all())
return Response({"data": queryset_filter.qs}, status=status.HTTP_200_OK)
Note that you will likely need to serialize the filtered queryset.

How to POST/PUT scraped data to RESTful API

I have a scraper that outputs JSON. I would like to programmatically read this output (e.g. on a daily basis) and deserialize it into my Django model, through a RESTful API like Tastypie. I would like to check for any duplicate entries / validate data before updating the model.
What is the best practice and most seamless way to do this?
--
JSON Output From Scraper (returns structured data)
Note: exchange_id is the foreign key of the Exchange object in my Django model
{
"website": "http://www.igg.com/",
"exchange_id": 1,
"ticker": "8002",
"full_name": "IGG Inc"
}
Django model
class Company (models.Model):
ticker = models.CharField(max_length=10, null=True)
full_name = models.CharField(max_length=200, null=True)
exchange = models.ForeignKey(Exchange, null=True)
website = models.URLField(null=True)
def __unicode__(self):
return self.ticker
def website_url(self):
if self.website:
return '%s' % (self.website, self.website)
else:
return ''
website_url.allow_tags = True
class Meta:
verbose_name_plural = "Companies"
I am going to assume that your app is private, and only you will have access to it. What you can do is implement django-restless with a model form.
from restless.http import Http201, Http400
from restless.views import Endpoint
from .forms import NewCompanyForm
class APIEndpoint(Endpoint):
"""
Endpoint for posting json data to server
"""
def post(self, request):
company_form = NewCompanyForm(request.data)
if company_form.is_valid():
# Check for duplicate data
# ...
if unique:
company_form.save()
return Http201({"message": "Post successful"})
else:
return Http400(reason='Data was not unique')
else:
return Http400(reason='You did not post a valid input')
Also, here is an example app using this library, https://github.com/dobarkod/django-restless/blob/master/testproject/testapp/views.py
As far as I understand, you need some tool to post/put data to your Service via RestAPI. Look at slumber. It very simple and very good interacting with tastypie.

Categories