Edit parameter description for Django REST swagger and other issues - django

I. I'm new to using Django REST Swagger for API documentation. I'm trying to understand how to edit a) 'Implementation Notes' and b) parameter descriptions. Image below -
Here's my viewset for the 'Crash' model.
class CrashViewSet(viewsets.ModelViewSet):
"""
retrieve: Get a single Crash instance
list: Get a list of all Crashes
"""
queryset = Crash.objects.all()
serializer_class = CrashSerializer
filter_backends = (SearchFilter,DjangoFilterBackend,OrderingFilter,)
search_fields = ('crash_id','crash_hr_short_desc','urb_area_short_nm','fc_short_desc',
'hwy_compnt_short_desc','mlge_typ_short_desc', 'specl_jrsdct_short_desc',
'jrsdct_grp_long_desc','st_full_nm','isect_st_full_nm','rd_char_short_desc',
'isect_typ_short_desc','crash_typ_short_desc','collis_typ_short_desc',
'rd_cntl_med_desc','wthr_cond_short_desc','rd_surf_short_desc','lgt_cond_short_desc',
'traf_cntl_device_short_desc','invstg_agy_short_desc','crash_cause_1_short_desc',
'crash_cause_2_short_desc','crash_cause_3_short_desc','pop_rng_med_desc','rd_cntl_med_desc')
filter_fields = ('ser_no','cnty_id','alchl_invlv_flg','crash_day_no','crash_mo_no','crash_yr_no','crash_hr_no',
'schl_zone_ind','wrk_zone_ind','alchl_invlv_flg','drug_invlv_flg','crash_speed_invlv_flg',
'crash_hit_run_flg',)
ordering_fields = '__all__'
I have made some edits to the DocString but don't quite understand how to proceed so as to get a more thorough descriptions of the endpoints and their fields. I tried going through the Django REST Swagger tutorial but I'm not given any direction on how to format the DOCSTRING. What's the best way to do this?
II. Another minor issue is that I want to get rid of the 'Django Login' button since this is public API. How do I do this?
III. What are some best practices for API documentation?

Related

"Cannot resolve keyword 'created' into field" on new DRF list view

I made a new list view in my Django REST Framework app:
class ColumnView(ListCreateAPIView):
queryset = Column.objects.all()
serializer_class = ColumnSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(user=self.request.user)
When I try to access it, I get the following error:
FieldError at /my/new/endpoint
Cannot resolve keyword 'created' into field. Choices are: _order, fields, from, my, model
There's no created field anywhere in sight - not in the ColumnSerializer, not in the Column Django model, nowhere. The stacktrace is really opaque, too - my app doesn't appear anywhere in it. What's going on?
Did you enable cursor pagination in your settings?
REST_FRAMEWORK = {
...
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination',
...
}
Be sure to read the docs on CursorPagination carefully:
Details and limitations
Proper use of cursor based pagination requires a little attention to detail. You'll need to think about what ordering you want the scheme to be applied against. The default is to order by "-created". This assumes that there must be a 'created' timestamp field on the model instances, and will present a "timeline" style paginated view, with the most recently added items first.
In other words, you can't have all three of these conditions:
Using cursor pagination
...without specifying an ordering
...on a model without a created field

Django rest framework hyperlinkrelatedfield for one table using primary key

I have a table called 'users' and 'location'. Users table has a foreign key that relates to location table. I have a users serializer to get the JSON. What would I do to get the hyperlinks for the users table using its primary key?
In django rest framework documentation, I couldn't find a solution. I tried using hyperlinkrelatedfield. But still I couldn't achieve this. Can someone help me in finding the solution?
Using rest-framework HyperlinkedRelatedField does not work because it was never built to expose the URL of the object being requested. Mainly because since the client already has the url of the user, why send it back again? Nevertheless you can achieve this by doing something like this.
class UserSerializer(serializers.ModelSerializer):
user_url = serializers.SerializerMethodField()
class Meta:
model = User
def get_label_location(self, obj):
return HyperlinkedRelatedField(view_name='user-detail',
read_only=True) \
.get_url(obj, view_name='label-detail',
request=self.context['request'], format=None)
Take note on a few things,
view-name param to the HyperlinkedRelatedField should be based on your url configuration
read-only has to be true since otherwise you'll have to specify the queryset. But since we have the object needed to generate the url we can ignore that.
I've set format param to None but you might want to set it based on your settings.
You can read up about SerializerMethodField here.

Returning related fields of a model instance

I am creating an app with a rest API that should return values for instances of objects based on the url given. Right now I have the API working using ModelViewSets of my objects for the API.
For example I have three objects, user, transactions, and goals.
As it stands I can go to /mysite/api/users and return a list of all users
I can also go to /mysite/api/users/1 to return just the user with the id '1'.
I can do something similar with transactions and goals.
What I'm looking to do is go to url /mysite/api/users/1/transaction/1/goal
to find the goal associated with the transaction for that user.
I've been scouring tutorials and am not sure what the right question is to ask in order to find something useful to learn how to do this. What is the correct way to go about setting up my rest api like this?
If I understand correctly, you want to create nested ressources.
If you are using Viewsets, then the ExtendedRouter class of the drf-extensions package will allow you to achieve this.
Drf-extensions documentation about this feature: https://chibisov.github.io/drf-extensions/docs/#nested-routes
There is also this module, who also offer the same features.
You can either use url params or query params to solve your issue. I will explain the URL params solution here,
serializers.py
#Write a Goal Serializer
urls.py
#change the URL according to your environment
url(r'^users/(?P<uid>[0-9]+)/transaction/(?P<tid>[0-9]+)/goal/$', GoalViewSet.as_view({'get': 'user_transaction_goal',}), name='user-transaction-goal'),
views.py
class GoalViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
queryset = Goal.objects.all()
def user_transaction_goal(self, request, uid, tid):
#assuming user is FK in transaction and transaction is a FK in goal
#modify the filter rule according to your model design
goals = Goal.objects.filter(transaction=tid, transaction__user=uid)
serializer = GoalSerializer(goals, many=False)
return Response(serializer.data)
As #clement mentioned you can also use plugins to handle this situation.

Django rest framework: automatically create a url for each field of a model

I have large table of data (~30 Mb) that I converted into into a model in Django. Now I want to have access to that data through a REST API.
I've successfully installed the Django REST framework, but I'm looking for a way to automatically create a URL for each field in my model. My model has about 100 fields, and each field has about 100,000 entries.
If my model is named Sample,
models.py
class Sample(models.Model):
index = models.IntegerField(primary_key=True)
year = models.IntegerField(blank=True, null=True)
name = models.TextField(blank=True, null=True)
...97 more fields...
then I can access the whole model using Django REST framework like this:
urls.py
class SampleSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Sample
fields = ( **100 fields**)
class SampleViewSet(viewsets.ModelViewSet):
queryset = Sample.objects.all()
serializer_class = SampleSerializer
router = routers.DefaultRouter()
router.register(r'sample', SampleViewSet)
But of course my browser can't load all of that data in a reasonable amount of time. I could manually make a different class and URL for each field, but there must be a better way... I want to be able to go to my_site.com/sample/year (for example) and have it list all of the years in JSON format, or my_site.com/sample/name and list all the names, etc.
Please help me figure out how to do this, thanks!
You might be able to do that using a custom viewset route.
You have this:
class ModelViewSet(ModelViewSet):
#list_route()
def sample_field(self, request):
desired_field = request.data.get('field', None)
if not desired_field:
return response # pseudocode
values = Model.objects.all().values_list(desired_field, flat=True)
# serialize this for returning the response
return Response(json.dumps(values)) # this is an example, you might want to do something mode involved
You will be able to get this from the url:
/api/model/sample_field/?field=foo
This extra method on the viewset will create a new endpoint under the samples endpoint. Since it's a list_route, you can reach it using /sample_field.
So following your code, it would be:
mysite.com/sample/sample_field/?field='year'
for example.
There are many interesting details in your question, but with this sample I think you might able to achieve what you want.
Try to use pagination. You can do it in almost the same way as in you question. Pagination in django lets you divide the results into pages. You don't have to display all the entries in the same page. I think this is the best option for you.
Refer django documentation on pagination:
Pagination in django

Tastypie: Filter Django comments based on the commented object

I use the django comments from contrib and I have an object (entry) that has some comments associated with it. In my tastypie resources I have:
class CommentResource(ModelResource):
user = fields.ForeignKey(UserResource, 'user')
class Meta:
queryset = Comment.objects.all()
resource_name = 'comments'
allowed_methods = ['get']
fields = ['comment', 'resource_uri', 'submit_date', 'user',]
filtering = {
'user': ALL_WITH_RELATIONS,
}
and I can get all the comments, or filter them by user. It's working ok.
Now I'm not sure, how would I do the same kind of filter but based on a certain entry object instead of user?
Thanks for your help.
Without knowing what is the relationship between the entry and comment it is hard to give a concrete answer but in a nut shell given that entry and comments are linked via manytomany relationship:
Create an EntryResource
Add the fields.ToManyField to the EntryResource for CommentResource
Add the `fields.ToOneField' to the CommentResource for EntryResource
Add 'comments' : ALL_WITH_RELATIONS to the filtering dict in EntryResource
Additionally, you could add a nested resource or a custom URL to the Comment to filter them based on an entry, but it all depends on your design.
Almost verbatim example for the above is given in Tastypie docs here.