I have simple blog app which have author=models.ForeignKey(User, editable=False) field. Blog posts are created from django admin site, and I use save_model to get author which is request.user.
Now I want that user (is_staff) can see only his own posts, when he browse model posts. But by default all blog posts are displayed, so how can I hide blog posts created by other users? Of course superusers need to see all of them.
Override the get_queryset method on the ModelAdmin subclass. The documentation has an example of exactly what you're asking for: displaying only objects related to the current user.
In case anyone is lazy. Here's a sample code I use.
So first you create a QuerySet Manager, I usually have mine in models.py, which does something like this (in this case I'm calling the model Blog):
class BlogManager(models.Manager):
def get_queryset(self, request):
query = Blog.objects.filter(author=request.user)
if request.user.is_superuser:
query = UserProfile.objects.all()
return query
Make sure your admin.py then has this:
def get_queryset(self, request):
queryset = BlogManager.get_queryset(self, request)
return queryset
So it might look like:
class BlogAdmin(admin.ModelAdmin):
#add any other code here
#Using that queryset manager created before
def get_queryset(self, request):
queryset = BlogManager.get_queryset(self, request)
return queryset
admin.site.register(BlogAdmin)
Hope this helps anyone!
Related
I should limit choices of manytomanyfield to logged in admin user profile, in django admin.
class News(models.Model):
title=models.CharField(max_length=200)
users=models.ManyToManyField(User, blank=True, limit_choices_to={"profile__school": "request.user.profile.school"})
I have tried to implement it in admin.ModelAdmin, where I can access request.user, but couldn't find a way to do it.
You don't do this in the model layer. Django's user layer is request unaware. Some code paths don't even have a request, for example if these are done through a Python script, or a Django management command.
You can limit the choices in the ModelAdmin by overriding get_form:
class NewsAdmin(ModelAdmin):
def get_form(self, request, obj=None, change=False, **kwargs):
form = super().get_form(request, obj=obj, change=change, **kwargs)
form.base_fields['users'].queryset = User.objects.filter(
profile__school__profile__user=request.user
)
return form
Note: It is normally better to make use of the settings.AUTH_USER_MODELĀ [Django-doc] to refer to the user model, than to use the User modelĀ [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
below is my code inside views.py. Here i don't required but what one should do if he also want staff member to allow update
#method_decorator(login_required(login_url='login'), name='dispatch')
class PostUpdate(UpdateView):
model = Post
form_class = PostForm
template_name = 'blog/post_form.html'
def get_queryset(self):
return super(PostUpdate, self).get_queryset().filter(user=self.request.user)
def get_success_url(self):
return reverse('dashboard')
You can write an owner mixin, as you check if the user is logged in, you can check if object.owner is equal to the user.
btw check object-level permission but decorating get_object method.
I have two models, User and Book. Users own books and can only be seen by their owners.
NOTE: the book model is handled in a separate database, so I can't use a foreign key on Book pointing to User. Not sure if this matters.
If I'm authenticated, and send a GET /books request, I want only the books owned by the user to be shown. If I'm not authenticated, I should get a 403 error.
Where should I implement this logic?
I could do it in the View, with something like this:
class BookView(APIView):
"""
Get books
"""
permission_classes = (IsAuthenticated, IsBookOwner,)
queryset = Book.objects.all()
serializer_class = BookSerializer
def post(self, request):
# create a book
def get(self, request):
books = Book.objects.filter(owner_id=request.user.owner_id)
serializer = self.serializer_class(books, many=True)
return Response(serializer.data)
class IsBookOwner(permissions.BasePermission):
"""
Object-level permission to only allow seeing his own books
"""
def has_object_permission(self, request, view, obj):
# obj here is a Book instance
return obj.owner_id == request.user.owner_id
Is this the correct way to do it? Also, is the IsBookOwner permission doing anything here?
User model dont have owner_id field. You should change request.user.owner_id to request.user.id
For get request you dont need IsBookOwner Permission. You already check owner in your queryset. if you need to check book owner entire view, it is okay.
books = Book.objects.filter(owner_id=request.user.owner_id)
I am trying to write a view in which a post can be created and in the same page, the object_list will be displayed. And even an object can be updated and deleted.
Country Capital
India Delhi UPDATE DELETE
USA Washington UPDATE DELETE
----- ------
I would appreciate helping me in achieve this or suggesting a similar type of question.
What you're looking for are Mixins.
Try creating a detail view class with the following parameters:
mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView
For example:
class ObjectDetail(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):
queryset = Object.objects.all()
As has proposed by Daniel, if you like DRF, ViewSets are also a decent alternative. However, they're not exactly succinct so I generally avoid them when possible.
Something like a ModelViewSet, however, is extremely clear-cut and the approach I generally choose.
Here's an example:
class ObjectViewSet(viewsets.ModelViewSet):
queryset = Object.objects.all()
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
Beautiful, isn't it?
For more details, see the DRF tutorial: http://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/
You are mixing view and template. View handle requests and template show content and links.
You will have ListView, which will contain list of posts. In template you add forms for update, form for create and forms for delete. Each form will have attribute action with link to proper view. So update forms will have link to url with UpdateView, create forms to CreateView, and delete to DeleteView. In each form you set redirect back to ListView. This way if you want to use only Django.
OR
If you really want to everything handle on one page without refreshing and redirecting. You can use ajax and django-rest-framework and its viewset. In viewset you can handle lists, create, update, push, detail, in one class.
Viewset:
class UserViewSet(viewsets.ViewSet):
"""
Example empty viewset demonstrating the standard
actions that will be handled by a router class.
If you're using format suffixes, make sure to also include
the `format=None` keyword argument for each action.
"""
def list(self, request):
pass
def create(self, request):
pass
def retrieve(self, request, pk=None):
pass
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
def destroy(self, request, pk=None):
pass
In my app I need to create products in a shop. So I have a model Shop and a model Product. I can see details about my shop in a DetailView ShopDetail. Now I need a CreateView in order to create products, but the url should be /shops/shop-id/products/create/, so I create products inside the shop. I guess it's something like
class ProductCreate(SingleObjectMixin, CreateView):
model = Product
def get_object(self, queryset=None):
return Shop.objects.get(id = self.kwargs['shop_id'])
Am I on the right track? :-D
No, you're not on the right track: the object returned by get_object should be a instance of the model; in fact, if you override get_object the model attribute becomes irrelevant.
There are a few approaches to this problem, but I would myself probably got for a single DetailView (with the Shop details), and add a form for Product to the template via the get_context_data method. The form's action attribute would, instead of being empty, point to the url to a separate CreateView which would handle the Product creation.
Alternatively you could simply display the Shop details through the get_context_data, which is simpler but mixes concerns (as the DetailView for shop is defined as a CreateView for Product).
I think you need:
from django.shortcuts import get_object_or_404
class ProductCreate(CreateView):
"""Creates a Product for a Shop."""
model = Product
def form_valid(self, form):
"""Associate the Shop with the new Product before saving."""
form.instance.shop = self.shop
return super(CustomCreateView, self).form_valid(form)
def dispatch(self, *args, **kwargs):
"""Ensure the Shop exists before creating a new Product."""
self.shop = get_object_or_404(Shop, pk=kwargs['shop_id'])
return super(ProductCreate, self).dispatch(*args, **kwargs)
def get_context_data(self, **kwargs):
"""Add current shop to the context, so we can show it on the page."""
context = super(ProductCreate, self).get_context_data(**kwargs)
context['shop'] = self.shop
return context
I hope it helps! :) You may wish to have a look at what the super-methods do.
(Disclaimer: Shameless self promotion.)