NestedSimpleRouter without using lookup in router - python

I am using drf-nested-router something like the following in my url.py
router = SimpleRouter()
profile_router = routers.NestedSimpleRouter(router, r'profile', lookup='user')
profile_router.register(r'comments', UserCommentViewSet, basename='profile-comments')
The viewset is
class UserCommentViewSet(CommentViewSet):
def get_queryset(self):
return Comment.objects.filter(owner=self.request.user)
So the URL is something like,
mydomain.com/profile/{profile_id}/comments/
it gives me right results. But the following URL is also giving me right results,
mydomain.com/profile/{anything}/comments/
because I am using the session user info to filter the data. Is it possible to make the URL like
mydomain.com/profile/comments/

Based on your code:
router = SimpleRouter()
# router.register('users', UserViewSet, 'user')
profile_router = routers.NestedSimpleRouter(router, r'profile', lookup='user')
profile_router.register(r'comments', UserCommentViewSet, basename='profile-comments')
You are interpreting this in a wrong way. Here's how you can use NestedSimpleRouter
mydomain.com/profile/ # list all the users.
mydomain.com/profile/{profile_id} # retrieve particular user based on profile_id/user_id/pk.
mydomain.com/profile/{profile_id}/comments/ # list all the comments belong to a particular user (not the current session user) based on profile_id/user_id.
mydomain.com/profile/{profile_id}/comments/{comment_id} # retrieve particular comment based on comment_id.
This url:
mydomain.com/profile/{anything}/comments/
is working because you are filtering by owner = request.user.
And this url:
mydomain.com/profile/{profile_id}/comments/
is supposed to give list of all comments by taking profile_id in UserCommentViewSet. So your view will be like:
class UserCommentViewSet(CommentViewSet):
def get_queryset(self):
return Comment.objects.filter(owner__id=profile_id)
In simple words you can use NestedSimpleRouter to get all users, user detail, all comments posted by single user and comment detail.
Solution:
If you need only current (session) user comments (since you dont need all comments by all users), you need something like:
router = SimpleRouter()
router.register(r'profile/comments', UserCommentViewSet, basename='profile-comments')
and the UserCommentViewSet is:
class UserCommentViewSet(CommentViewSet):
def get_queryset(self):
return Comment.objects.filter(owner=self.request.user)
Then, this url:
mydomain.com/profile/comments/
will give all comments as required.

Related

Returning a filter result in a different page - Django

I've searched up but nothing seams to do the trick or be on point.
Let's say i have two websites on page A.html I have a filter (djnago_filter) and B.html is empty. If the user submits the search form he is redirected from page A.html to page B.html where the results are displayed with for loop.
How do I to that?
I was thinking about passing the data from query set to another view but there must be better solution to this.
There are several ways to store and display information on the different views(pages):
You could use the Sessions:
#views.py
#set the session key in the view A:
request.session['info'] = {'key', 'value'}
#get the session key in the view B:
info = request.session['info']
del request.session['info']
You could use Models with JSONField:
#app/models.py
#model to store information from page A
from django.contrib.auth.models import User
class MyModel(models.Model):
user = models.ForeignKey(User,
on_delete=models.DELETE,
related_name="info")
info = models.JSONField()
#views.py
#store data in the view A:
from app.models import MyModel
MyModel.objects.create(user=request.user, info={'key', 'value'})
#retrieve data in the view B:
info = MyModel.objects.get(user=request.user)
But actually, all depends on the logic that you intent to achieve. There are actually other methods like using async Ajax, React, WebSockets...
you can pass it as an HTML request parameter inside the url, for example, in your pageA template will be
to page B
and inside pageb view, you can take the filter from the request object
def page_b_view(request):
some_filter = request.GET.get('some_filter')

How can I restrain acess for my profile view?

I did a view to access personal information. To do this, there is a pk in the URL. However, this is problematic because they can access other user info just by changing the value of the pk. I read the doc and I didn't find anything related to that.
How can I prevent this problem?
path('profil/<int:pk>', views.ProfilView.as_view(), name="profil")
If there is no reason for the PK to be in the URL (i.e. you don't want to use the same view to view others' information), you can make your ProfilView look something like this, assuming it derives from DetailView:
from django.contrib.auth.mixins import LoginRequiredMixin
# ...
class ProfilView(LoginRequiredMixin, DetailView):
model = User # or whatever it happens to be
def get_object(self):
return self.request.user # Always return the current user
and simply
path('profil/', views.ProfilView.as_view(), name="profil")
in your URL configuration.
Require the user to be logged in, and display a 401 Unauthorized error if it's not their own profile id.
It might be useful have a profil/me url that always shows the user's own profile.

Django REST API doint filter and on the same field

I'm all new to django REST API and trying to understand something.
when using a URI like this:
http://example.com/api/products?category=clothing&category=shoes
I would have wanted to recieve back all the category that are clothing and shoes but at the ened the only thing that I get is all the shoes
what is the correct way to make it act as needed by me?
Thank you in advance!!!
The documentation at DRF (Django Rest Framework) should help you with what you're trying to do:
In essence you need to override the queryset method and look for query_params:
Below is an example from DRF's documentation:
class PurchaseList(generics.ListAPIView):
serializer_class = PurchaseSerializer
def get_queryset(self):
"""
Optionally restricts the returned purchases to a given user,
by filtering against a `username` query parameter in the URL.
"""
queryset = Purchase.objects.all()
username = self.request.query_params.get('username', None)
if username is not None:
queryset = queryset.filter(purchaser__username=username)
return queryset

Django and one-time links with data

I think I have a simple case here but I'm not finding good examples of the implementation ( or probably failing to understand).
After the user (not logged in) types his username to a form, Django would generate a unique URL based of this data (encoded in URL?) for the user that can be accessed once and within 5 minutes. Based on that URL (after clicking it) the data (username) would be decoded and ready for use in this one-time view.
Simple scenario if needed: user nimda fills the form and then is redirected (for example) to a view that shows the generated URL. Then nimda clicks the generated URL and a view is shown with the data he or she typed into the form
If you don't need that url you could save data to the session and send the user to a specific url.
The view connected to the url generates content depending on the (anonymous) users session. The user can see the content as long as you sessions last or you implement a time stamp an check this before delivering content.
If you need the url:
Build a model connected with the sessions with url and a time stamp.
Configure the urls.py for the url-model like
url(r'^dataUrl/(?P[0-9]+)/$', PostDelete.as_view()),
Assign the user session and the entered data (saved to the session) with
the url-model.
When delivering the content check for the random-url-part, and the timestamp and deliver the date (or not ;) )
You can access the session in a cvb's like this:
class YourClassName(TemplateView):
template_name = ""
def get_context_data(self, **kwargs):
context = super(YourClassName , self).get_context_data(**kwargs)
DataYouNeed = self.request.session["SessionVariableOfTheUser"]
userDAta = self.request.user #if this is usefull `
or in a createView:
class URLCreate(CreateView):
model = randomUrl
template_name = "entryCreate.html"
success_url = "../xyz/"
form_class = UrlCreateForm
# if you like to change the success-url
def get_success_url(self):
#print dir(self.object.instance)
#print self.object.instance.id
url = "../bringMeTo/%s" % self.object.instance.id
return url
def form_valid(self,form):
form.instance.user = self.request.user
self.request.session["formData"]= form.instance
return super(URLCreate, self).form_valid(form)
pass
This is not a ready solution! Just an inspiration for a start.

Django Rest Framework #detail_route yields 404

I have a ViewSet like
class CustomerViewSet(ModelViewSet):
queryset = models.Customer.objects.all()
serializer_class = serializers.CustomerSerializer
filter_class = filters.CustomerFilterSet
#detail_route
def licenses(self, request, pk=None):
customer = self.get_object()
licenses = Item.objects.active().sold().for_customer(customer)
serializer = serializers.ItemSerializer(licenses, many=True)
return Response(serializer.data)
Strapped into urls.py with router.register(r'customers', views.CustomerViewSet)
i can GET /api/customers and GET /api/customers/1000, but GET /api/customers/1000/licenses is not found. It gives me 404. The flow never enters the licenses method.
I looked at similar questions here, but they used an incorrect signature, which I do not: def licenses(self, request, pk=None)
python 3.4.0
djangorestframework==3.2.3
EDIT: Once again, I find my answer less than a minute after asking... Apparantly, the decorater needs parenthesees like #detail_route(). I thought these were always optional by convention..?
For a post request you'll need to specify the method because the detail_route decorator will route get requests by default.
Like so: #detail_route(methods=['POST'])
By default, router adds trailing slash to the url. So, GET /api/customers/ would be working instead of GET /api/customers. If you don't want to use trailing slash you can pass trailing_slash = False to router initializer.
E.g.-
router = DefaultRouter(trailing_slash = False)
router.register(r'customers', views.CustomerViewSet)
If that does not works, then there is a problem with the way you are importing router urls into main urls.

Categories