Django REST framework not looking for URL encoded detail views - python

I have a model whose Primary Key is a TextField. This is a minimalistic reproduction of my issue:
Model:
class Filename(models.Model):
path = models.TextField(primary_key=True)
Serializer:
class FilenameSerializer(ModelSerializer):
class Meta:
model = Filename
fields = '__all__'
View:
class FilenameViewSet(ModelViewSet):
queryset = Filename.objects.all()
serializer_class = FilenameSerializer
I'm using a DefaultRouter for URLs. Here is the problem: If I sent
{"path":"test"} with a POST /filename/ I can perfectly retrieve my object with GET /filename/test/ as you would expect. However, if I POST /filename/ something like {"path":"c:\\test"} I would expect either GET /filename/c%3A%5Ctest/ or GET /filename/c%3A%5C%5Ctest/ to be the proper way to get it, but none of those works. Does anybody knows what's going on?
Update: The webserver logs show Not Found: /filename/c:\test so it's being decoded properly at some moment. Maybe some URL regex issue?

The issue was not with any URL encoded in general, but with the dot (%2E) in particular. The DefaultRouter() does not match the dots by default. This behavior can be modified setting the lookup_value_regex attribute in the ViewSet.
class FilenameViewSet(ModelViewSet):
queryset = Filename.objects.all()
serializer_class = FilenameSerializer
lookup_value_regex = '[^/]+'

hey you have to define the lookupfield , you can overide the get_object change string format to a norma text
class FilenameViewSet(ModelViewSet):
queryset = Filename.objects.all()
serializer_class = FilenameSerializer
lookup_field = 'path'
def get_object(self):
try:
from urllib import unquote
except ImportError:
from urllib.parse import unquote
path = unquote(kwarhs['path'))
return self.get_queryset.get(path=path)

if im getting your question correctly . you problem is with json format .
On adding escape character \ before special characters would resolve your issue.
see this link below:
How to escape special characters in building a JSON string?

Related

How to obtain content_type from a Django Model Class?

I am trying to obtain the ContentType for a django models.Model. Not trying to get the model from a ContentType, which is all I can find in the docs. For example:
model_name = 'FooBar'
MyModel = apps.get_model('app_xyz', model_name)
MyModel
<class 'apps.app_xyz.models.FooBar'>
How do I get the ContentType of MyModel? The only thing I can figure out to do is set the model_name string to lower and directly query ContentTypes:
ct = ContentType.objects.get(model=model_name.lower())
That seems really fragile, and using lower() has a code smell I don't like. I've looked through FooBar's methods, _methods, and __methods to no avail. Thanks!
from django.contrib.auth.models import User
cc = ContentType.objects.get_for_model(User)
docs

Django REST: serialize url to list of objects of a category

Hard facts:
I am using Django 2.0 with python 3.6, if it makes any difference.
What I am trying to achieve is a link to a list of objects that belong to a summary.
I have a ManyToOne relationship in my models.py.
class Summary(models.model):
type=models.CharField
class Object(models.Model):
summary= models.ForeignKey(Summary, on_delete=models.CASCADE)
in urls.py
object_list= views.ObjectListViewSet.as_view({
'get': 'list'
})
urlpatterns = format_suffix_patterns([
url(r'^summary/(?P<pk>[^/.]+)/objects/$', object_list, name='summary-objects')
])
and now the idea was to give a user the possibility to click the an url in the browsable API and getting all objects.
So, I tried to write a MethodField in serializers.py. I am not able to get any reasonable URL here, the only solution would be to hardcode it.
class SummarySerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name="app:summary-detail")
objects= serializers.SerializerMethodField('get_obj_url')
def get_obj_url(self, obj):
pass
class Meta:
model = Summary
Is this possible?
Is it necessary to write a MethodField?
If yes, how do I get the url I need?
Actually, reverse, as suggested in the comments, does the trick.
The solution is:
def get_obj_url(self, obj):
request = self.context.get('request')
return request.build_absolute_uri(reverse('api-root')) + 'summary/{id}/objects'.format(
id=obj.id)
EDIT:Typo

Django REST Framework hyperlink URL failing to resolve

I am trying to set up hyperlinking in my Django REST Framework API, and for the life of me I can't find out where my error is.
My model:
class Franchise(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
# Other fields
My serializer
class FranchiseListSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='franchise_details',
lookup_field='id',
lookup_url_kwarg='franchiseid'
)
class Meta:
model = Franchise
fields = ('id', 'name', 'url')
My URLs:
url(r'^db/franchise/$', views.franchise_index, name='db_franchise_index'),
url(r'^db/franchise/(?P<franchiseid>[0-9]+)/$', views.franchise_details, name='db_franchise_details')
Note that this is an included url conf, all my api functionality goes within an /api/ url
My views:
#api_view(['GET'])
def franchise_index(request, format=None):
franchise_list = Franchise.objects.all()
serializer = FranchiseListSerializer(franchise_list, many=True, context={'request': request})
return Response(serializer.data)
#api_view(['GET'])
def franchise_details(request, franchiseid, format=None):
franchise = Franchise.objects.get(id=franchiseid)
serializer = FranshiseDetailSerializer(franchise)
return Response(serializer.data)
Note that FranshiseDetailSerializer seen above works just fine.
Summary:
URL /api/db/franchise/ goes to the view franchise_index, which returns data serialized by FranchiseListSerializer.
URL /api/db/franchise/<franchiseid>/ goes to the view franchise_details, which returns data serialized by FranchiseDetailSerializer (Works fine)
As you can see, I have added a url field to FranchiseListSerializer, which I supposed to link to the corresponding franchise details page. Before I added the url field, the serializer only returned id and name, which was the correct and expected behaviour at the time.
When I go to /api/db/franchise/ now, I get the error:
ImproperlyConfigured at /api/db/franchise/
Could not resolve URL for hyperlinked relationship using view name "franchise_details". You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field.
Following this hint, I set up the arguments in the url field of FranchiseListSerializer, and as far as I can tell, they are correct. I have checked and double checked the DRF documentation, here, here and here, but have found no solution.
Following other similar issues on Stackoverflow, I tried changing view_name='franchise_details' to view_name='api:franchise_details' (the name of the Django app the relevant files are in) and view_name='api:franchise_details-detail', but to no avail.
Any and all help is appreciated, cheers.
Thanks to #AKS's promting, I figured it out. view_name is actually the name of the URL, not the view. From the way I read the documentation (and the fact that it is view_name not url_name), it seemed to say that it was supposed to be the name of the view.
I had actually tried using view_name='db_franchise_details' (my urls name) before, but that did not work. After AKS promted my with that comment, I tried again, and also tried using view_name='api:db_franchise_details', which does work!

Reverse URL by more than one parameter in Django REST Framework

Let's take the usual Customers with Orders pair of models.
How can we make a HyperlinkedModelSerializer of Order url field reverse to a url like /customers/<customer_pk>/orders/<order_id>/?
I thought of using the lookup_field in the Meta class but it seems to only accept one field.
Thanks for your help
HyperlinkedModelSerializer uses a single parameter in the lookup_field as of DRF 2.0, so like you, I wasn't able to use this.
However, with some tricks I picked up on this question I was able to build a ModelSerializer that would have a url field that contained the reverse URL as you describe.
class OrderSerializer(serializers.ModelSerializer):
# Fields, etc
url = serializers.SerializerMethodField('make_url')
class Meta:
model = Order
# Class info here as usual
def make_url(self, obj):
"""
Build URL for Order instance
"""
# Prepare the IDs you need for the URL reverse
kwargs = {
'customer_pk': obj.customer.id,
'order_id': obj.id,
}
url = reverse('api:single_order', kwargs=kwargs)
return self.context['request'].build_absolute_uri(url)
If you're using viewsets, then this library will help you out: drf-nested-routers.

Django Tastypie Override URL with slug

I have a similar coce:
def override_urls(self):
return [
url(r"^(?P<resource_name>%s)/(?P<slug>[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
]
Which produces an URL like:
/api/v1/nodes/<slug>/
Everything fine except that self.get_resource_uri(bundle) returns /api/v1/nodes/<id>/ and I cannot compare the current URL with the resource URI effectively.
What am I doing wrong?
Solution: working code
I implemented the proposed solution here:
https://github.com/ninuxorg/nodeshot/blob/refactoring/nodeshot/core/base/resources.py
Any additional feedback for improvement is welcome.
You could override get_resource_uri on your resource to return the correct uri. After all, the correct one is the one with the slug since that is the (first) one captured by your resource.
Update
The right way to do this is actually to skip override_urls and put this on the Resource's Meta:
detail_uri_name = 'slug'
TLDR Background
I dug a bit deeper and it looks like there's a much better place to implement this. The default implementation of get_resource_uri (indirectly) calls self.detail_uri_kwargs and then reverse.
The default implementation of detail_uri_kwargs in ModelResource just looks up self._meta.detail_uri_name (whose name is not intuitive), and grabs that key off the model. detail_uri_name defaults to pk.
If you just provide a different name here, you can skip the override_urls and the get_resource_uri!
Something like this (building on the code linked in comments by the OP):
from tastypie.resources import ModelResource
from tastypie.bundle import Bundle
class BaseSlugResource(ModelResource):
""" Base Model Resource using slug urls """
class Meta:
abstract = True
detail_uri_name = 'slug'
I'm not sure off the top of my head whether resource Metas are inherited (I'm going to guess they're not), so this may not work as a base class. Luckily, the one line required is probably fine to paste into each Resource that needs it.
I'd like to clean it up with a working example even though the #dokkaebi's answer is marked (and partially) correct. The only missing part is you still have to prepend url that will be resolved for listing and such.
from tastypie.resources import ModelResource
from myapp.models import SomeModel
class BaseSlugResource(ModelResource):
""" Base Model Resource using slug urls """
class Meta:
queryset = SomeModel.objects.all()
detail_uri_name = 'slug'
def prepend_urls(self):
return [
url(r'^(?P<resource_name>%s)/(?P<slug>[\w\.-]+)/$' % self._meta.resource_name, self.wrap_view('dispatch_detail'), name='api_dispatch_detail'),
]
This will display the correct resource_uri's for listing as well as resource get. However, you'll most likely loose {schema} resource (i.e. /api/posts/schema/) as it's treated as a slug too.

Categories

Resources