Django Rest Framework URL parameters - django

Currently i have to display all customers for a specific workshop then I am using following url:
http://localhost:8000/customer-list/?workshop_id=1
and in my view I have following implementation:
class CustomerList(ListAPIView):
queryset = Customer.objects.all()
serializer_class = CustomerSerializer
filter_backends = [SearchFilter]
search_fields = ['customer_name']
def filter_queryset(self, queryset):
workshop_id = self.request.query_params.get('workshop_id', None)
if workshop_id is not None:
queryset = queryset.filter(workshop_id=workshop_id)
return queryset
def list(self,request,*args,**kwargs):
workshop_id = self.request.query_params.get('workshop_id', None)
if not (workshop_id):
return Response({"status": "Required field not found."},
status=status.HTTP_404_NOT_FOUND)
return super(CustomerList, self).list(request,*args,**kwargs)
Url Path looks like this:
path('customer-list/', views.CustomerList.as_view(),name='customer_list'),
But I want my url should look like this:
http://localhost:8000/{workshop_id}/customer-list
How can I set my URL path:
and how can I get workshop_id in customer view to apply filters:
I have tried to change url pattern but it did not work.

To pass workshop_id as an URL parameter just define it in the path, for example:
path('<int:workshop_id>/customer-list/', views.CustomerList.as_view(),name='customer_list')
And you can get the parameter from self.kwarg:
class CustomerList(ListAPIView):
...
def list(self, request, *args, **kwargs):
workshop_id = self.kwargs.get('workshop_id', None)
...

Related

Broken images in Django

I have a problem with my images, which started when I changed a part of my code in view.p (API)
from:
class PostListView(ListAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
to:
#api_view(['GET'])
def PostListView(request, *args, **kwargs):
queryset = Post.objects.all()
username = request.GET.get('username')
if username != None:
queryset = queryset.filter(author__username__iexact=username)
serializer = PostSerializer(queryset, many=True)
return Response(serializer.data, status=200)
I did this because I wanted to pass "username" into it and I dont know how to do that using APIView, so i used this, but then my images are broken and i notice with the APIView, the images url starts from "127.0.0.1/8000/..." but with this new view the url is "localhost/...." which i think is the problem.
How do i go about it please
Pass request to PostSerializer's context
For ex:
serializer = PostSerializer(queryset, many=True, context = {'request':request})

Django - How to change the default queryset in a view if a filter is used? [duplicate]

In ListView, I can easily use def post(self, request) method to make a post request from a list view. But I want to make the post request from def get_queryset(self) which I am not yet able to do. When I try to do it it shows "method 405 not allowed!" even though post method is allowed through http_method_names.
How can I access POST request inside get_queryset function?
class ZonListView(SearchMixin, SingleTableMixin, ListView):
template_name = 'cadmin/list.html'
model = Zon
table_class = ZonTable
search_fields = {
'title': 'icontains',
'description': 'icontains',
}
def post(self, request): # ***** this one works! ******
try:
toggle_status = request.POST.get('toggle-status')
pk = int(request.POST.get('pk'))
....
return HttpResponseRedirect(reverse('cadmin:zon_list'))
def get_queryset(self):
qs = super(ZonListView, self).get_queryset()
if self.request.POST: #***** Not working. 405 Error *****#
try:
toggle_status = self.request.POST.get('toggle-status')
pk = int(self.request.POST.get('pk'))
......
if self.request.GET:
try:
status = self.request.GET.get('status')
qs = qs.filter(status=status)
except Exception:
pass
return qs.distinct()
def get_context_data(self, **kwargs):
....
To make method allowed you need to implement function named same as method, post in your case. So to use request.POST in get queryset you also need to define post() method like this:
def post(self, request): # ***** this method required! ******
self.object_list = self.get_queryset()
return HttpResponseRedirect(reverse('cadmin:zon_list'))
def get_queryset(self):
qs = super(ZonListView, self).get_queryset()
if self.request.POST: #***** Now allowed *****#
try:
toggle_status = self.request.POST.get('toggle-status')
pk = int(self.request.POST.get('pk'))
......
Look Django's View source to check how allowed methods defined.

Django passing multiple ids through URL

For POST/GET (etc) requests I have the following URL for one user:
v1/users/userid123
registered as so:
router.register(r"v1/users", accounts_views_v1.UserViewSet)
What modifications should I make so that I can pass multiple user IDs like:
v1/users/?ids=["userid123","userid456"]?
It worked without any modification for another model of mine, with the same criteria, but this one keeps giving a 403 error (before even going into the method!) when I try it
Code per request:
My viewset is insanely long, here's the beginning though
class UserViewSet(MultipleDBModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.none()
#workspace_specific
def get_queryset(self):
group_ids = json.loads(self.request.query_params.get("group_id", "[]"))
queryset = None
if group_ids:
queryset = User.objects.filter(group_memberships__group__in=group_ids).distinct()
else:
queryset = User.objects.all()
return queryset.select_related("workspace_role").prefetch_related(
"group_memberships__group_role", "group_memberships__group"
)
and my URLS:
PREFIX = settings.REST_FRAMEWORK_ROUTER_PREFIX
if PREFIX:
PREFIX = r"^" + str(PREFIX) + r"/"
router = BulkRouter()
single_object_router = SingleObjectRouter()
lazy_single_object_create_or_update_router = LazySingleObjectCreateOrUpdateRouter()
...
router.register(r"v1/users", accounts_views_v1.UserViewSet)
...
urlpatterns = [...]
GET method:
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True, exclude=None)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True, exclude=None)
return Response(serializer.data)
I found the kink, this function wraps all the others:
def permission_check_wrapper(self, request, *args, pk=None, **kwargs)
of course, when there are IDs instead of one pk, it doesn't work - how can I pass ids instead?

How to use django-el-pagination with filtering?

I have a django Listview and i am using django-el-pagination's ajax endless pagination to paginate my results and it works well. The problem is I need to apply some filters to it and have no clue on how to do it.
Can i send parameters through GET to the view? Have searched a lot and seems like no one had this problem before.
Data is always returned in the get_queryset() function.
If you want to simply filter from the database, you can just return objects.
class IndexView(AjaxListView):
template_name = '****'
context_object_name = '****'
page_template = '****'
def get_queryset(self):
return your_model.objects.filter(title='***').order_by('**')
Else if you want to get data from non-database, you need to implement a proxy accordding to this answer.
If not,the pagination ajax will request all data, then slice it.The proxy make your data sliced while querying.
This is my filter that getting data from ElasticSearch.
class IndexView(AjaxListView):
template_name = '****'
context_object_name = '****'
page_template = '****'
def get_queryset(self):
params = {}
# get query params
for item in self.request.GET.items():
if item[0] == 'page' or item[0] == 'querystring_key':
continue
params[item[0]] = item[1]
# no filter
if len(params) == 0:
return ****.objects.filter().order_by('-date')
else:
return ESResult(params)
class ESResult(object):
def __init__(self, params):
self.params = params
def __len__(self):
s = self.search_es()
if s:
s = s[:1]
r = s.execute()
return r['hits']['total']
else:
return 0
def __getitem__(self, item):
assert isinstance(item, slice)
result = []
s = self.search_es()
if s:
s = s[item.start:item.stop] # slice before querying
r = s.execute()
for a in r.to_dict()['hits']['hits']:
one = a['_source']
one['id'] = int(a['_id'])
result.append(one)
return result
def search_es():
...
# filter request here
...
The list object used by AjaxListView is defined by get_queryset() method. To filter the queryset based on the users input, you may refer to POST method:
from app.forms import BlogFilterForm
class Blog(LoginRequiredMixin, AjaxListView):
context_object_name = "posts"
template_name = 'blog/blog.html'
page_template = 'blog/post_list.html'
success_url = '/blog'
def get_queryset(self): # define queryset
queryset = Post.objects.all() # default queryset
if self.request.method == 'POST': # check if the request method is POST
form = BlogFilterForm(self.request.POST) # define form
if form.is_valid():
name = form.cleaned_data['name'] # retrieve data from the form
if name:
queryset = queryset.filter(name=name) # filter queryset
else:
queryset = queryset
return queryset
def get_context_data(self, **kwargs):
context = super(Blog, self).get_context_data(**kwargs)
context['form'] = BlogFilterForm() # define context to render the form on GET method
return context
def post(self, request, *args, **kwargs): # define post method
return super(Blog, self).get(request, args, kwargs)
The endless pagination should work fine.
using filters with django-endless-pagination

DjangoRestFramework - How to filter ViewSet's object list based on end-users input?

This is my ViewSet:
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.order_by('-createdAt')
serializer_class = PostSerializer
permission_classes = (IsAuthenticated, IsLikeOrOwnerDeleteOrReadOnly,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user, location=self.request.user.userextended.location)
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self,
'location': self.request.user.userextended.location
}
#detail_route(methods=['post'], permission_classes=[IsFromLocationOrReadOnly])
def like(self, request, pk=None):
post = self.get_object()
post.usersVoted.add(request.user)
return Response(status=status.HTTP_204_NO_CONTENT)
This is my router / urls.py:
router = routers.DefaultRouter()
router.register(r'posts', views.PostViewSet)
So when I go to /posts I get a list of all posts. What I want is to be able to allow the end-user to go to a specific URL like so: /posts/username and when he does, I want to give him all the posts of that specific username (the filtering will be simple. Something along these lines:
queryset = Post.objects.filter(username=usernameProvidedByTheURL)
How do I go about doing this? Is it possible using DRF?
In your url:
url(r'^/post/(?P<username>\w+)/?$', PostViewSet.as_view({'get': 'list'})),
Then in your PostViewSet, overwrite the get_queryset() method to filter the data by username
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.order_by('-createdAt')
serializer_class = PostSerializer
permission_classes = (IsAuthenticated, IsLikeOrOwnerDeleteOrReadOnly,)
def get_queryset(self):
username = self.kwargs['username']
return Post.objects.filter(username=username)
UPDATE
If you want to keep /post/ endpoint to retrieve all post. Then you need to create an extra view to handle /post/username
class PostsListByUsername(generics.ListAPIView):
serializer_class = PostSerializer
def get_queryset(self):
username = self.kwargs['username']
return Post.objects.filter(username=username)
Then in your urls.py
url(r'^/post/(?P<username>\w+)/?$', PostsListByUsername.as_view()),
Note:
In your get_serializer_context method, you don't need to return request, format and view. DRF will append it for you.
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'location': self.request.user.userextended.location
}