I'm working in a CRUD and one of the features of the list is bulk delete. The user chooses (checkbox) the lines he wants to delete and press the "Delete selected" button.
I'm using generic views in my CRUD. My single delete, for example is like this:
class ContentDeleteView(NextRedirectMixin, DeleteView):
model = Content
template_name = 'content_delete.html'
def get_success_url(self):
messages.add_message(self.request, messages.SUCCESS, "Content deleted successfully.")
return reverse('content:content-detail')
My problem is creating the bulk delete view. Is there something from Django core features that I could use to bulk delete? I would like to avoid installing an app for that.
Thanks for any help
You should resort to rolling out your own kind of delete view, here is a basic example:
class BulkDeleteView(View):
model = None
def post(self, request, *args, **kwargs):
delete_ids = request.POST['delete_ids'].split(',') # should validate
self.model.objects.filter(pk__in=delete_ids).delete()
return render / redirect
So the basic idea is to subclass View and roll out your own implementation of a base BulkDeleteView
Related
I'm still new to Django and DRF. I have 2 models (Policy and Rescue) and Rescue is related to Policy by the Foreign Key policy_id. I have no issue to POST a JSON message and get Policy populated with the request data by CreateView. However, the 2nd model Rescue needs be populated based on some calculations from the JSON data POSTed to the Policy. Rescue cannot be POSTed beforehand. I tried hard but had no clue to do so.
Is this something to do with nested serializer or something else?
I've tried to
Can I try this way: inside the class CreateView:
class CreateView(generics.CreateAPIView):
def create(self, request, *args, **kwargs):
my_serializer = self.get_serializer(data=request.data)
...
# get a policy object based on 'policy_id' against serializer
my_policy = Policy.objects.get(policy_id=my_serializer.data['policy_id'])
...
... # some calculations to work out a rescue id, and will be returned and saved.
Rescue.objects.create(rescue_id='QD1234', policy=my_policy)
you can use a generic CreateAPIView and override the perform_create method.
def perform_create(self, serializer):
my_policy = serializer.save()
# you custom calculation for rescue_id
rescue_obj = Rescue.objects.create(rescue_id='QD1234', policy=my_policy)
perform create method is documented here: https://www.django-rest-framework.org/api-guide/generic-views/#methods
I have a model named Post and have a field there called owner (foreign key to User). Of course, only owners can update or delete their own posts.
That being said, I use login_required decorator in the views to make sure the user is logged in but then, I also need to make sure the user trying to update/delete the question is the owner.
As I'm using Django: Generic Editing Views the documentation says I need to use Django: UserPassesTestMixin.
This validation will be done for the update and delete views. DRY, what is the way to go about this? should I create a class named TestUserOwnerOfPost and create a test_func() and then make the update and delete views inherit from it?
Cause that's what I have tried and didn't work, code below:
from django.views.generic.edit import UpdateView
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import UserPassesTestMixin
class TestUserOwnerOfPost(UserPassesTestMixin):
def test_func(self):
return self.request.user == self.post.owner
class EditPost(UpdateView, TestUserOwnerOfPost):
model = Post
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(EditPost, self).dispatch(*args, **kwargs)
With the code above, every logged-in user in the system can edit/delete any post. What am I doing wrong? am I missing something? thanks.
The first problem is that the order of the classes you inherit is incorrect, as #rafalmp says.
However, fixing that doesn't solve the problem, because the UserPassesTest mixin performs the test before running the view. This means that it's not really suitable to check the owner of self.object, because self.object has not been set yet. Note I'm using self.object instead of self.post -- I'm don't think that the view ever sets self.post but I might be wrong about that.
One option is to call self.get_object() inside the test function. This is a bit inefficient because your view will fetch the object twice, but in practice it probably doesn't matter.
def test_func(self):
self.object = self.get_object()
return self.request.user == self.object.owner
Another approach is to override get_queryset, to restrict it to objects owned by the user. This means the user will get a 404 error if they do not own the object. This behaviour is not exactly the same as the UserPassesTestMixin, which will redirect to a login page, but it might be ok for you.
class OwnerQuerysetMixin(object):
def get_queryset(self):
queryset = super(OwnerQuerysetMixin, self).get_queryset()
# perhaps handle the case where user is not authenticated
queryset = queryset.filter(owner=self.request.user)
return queryset
The order of the classes you inherit from matters. For your access control to work, it must be enforced before UpdateView is executed:
class EditPost(TestUserOwnerOfPost, UpdateView):
I am using Django Rest framework. I want to serialize multiple models and send them as a response. Currently I can send only one model per view (like CartView below sends only Cart object). Following models (unrelated) can be there.
class Ship_address(models.Model):
...
class Bill_address(models.Model):
...
class Cart(models.Model):
...
class Giftwrap(models.Model):
...
I tried using DjangoRestMultipleModels, it works ok but has some limitations. Is there any in-built way? Can't I append to the serializer that's created in the following view?
from rest_framework.views import APIView
class CartView(APIView):
"""
Returns the Details of the cart
"""
def get(self, request, format=None, **kwargs):
cart = get_cart(request)
serializer = CartSerializer(cart)
# Can't I append anything to serializer class like below ??
# serializer.append(anotherserialzed_object) ??
return Response(serializer.data)
I really like DRF. But this use-case (of sending multiple objects) makes me wonder if writing a plain-old Django view will be better suited for such a requirement.
You can customize it, and it wouldn't be too weird, because this is an APIView (as opposed to a ModelViewSet from which a human being would expect the GET to return a single model) e.g. you can return several objects from different models in your GET response
def get(self, request, format=None, **kwargs):
cart = get_cart(request)
cart_serializer = CartSerializer(cart)
another_serializer = AnotherSerializer(another_object)
return Response({
'cart': cart_serializer.data,
'another': another_serializer.data,
'yet_another_field': 'yet another value',
})
Basically what I'm trying to achieve is a multi-model django app where different models take advantage of the same views. For example I've got the models 'Car' 'Make' 'Model' etc and I want to build a single view to perform the same task for each, such as add, delete and edit, so I don't have to create a seperate view for add car, ass make etc. I've built a ModelForm and Model object for each and would want to create a blank object when adding and a pre-populated form object when editing (through the form instance arg), with objects being determined via url parameters.
Where I'm stuck is that I'm not sure what the best way to so this is. At the moment I'm using a load of if statements to return the desired object or form based on parameters I'm giving it, which get's a bit tricky when different forms need specifying and whether they need an instance or not. Although this seems to be far from the most efficient way of achieving this.
Django seems to have functions to cover most repetitive tasks, is there some magic I'm missing here?
edit - Here's an example of what I'm doing with the arguments I'm passing into the url:
def edit_object(request, object, id):
if(object==car):
form = carForm(instance = Car.objects.get(pk=id)
return render(request, 'template.html', {'form':form})
What about using Class Based Views? Using CBVs is the best way in Django to make reusable code. For this example maybe it can be a little longer than function based views, but when the project grows up it makes the difference. Also remember "Explicit is better than implicit".
urls.py
# Edit
url(r'^car/edit/(?P<pk>\d+)/$', EditCar.as_view(), name='edit-car'),
url(r'^make/edit/(?P<pk>\d+)/$', EditMake.as_view(), name='edit-make'),
# Delete
url(r'^car/delete/(?P<pk>\d+)/$', DeleteCar.as_view(), name='delete-car'),
url(r'^make/delete/(?P<pk>\d+)/$', DeleteMake.as_view(), name='delete-make'),
views.py
class EditSomethingMixin(object):
"""Use Mixins to reuse common behavior"""
template_name = 'template-edit.html'
class EditCar(EditSomethingMixin, UpdateView):
model = Car
form_class = CarForm
class EditMake(EditSomethingMixin, UpdateView):
model = Make
form_class = MakeForm
class DeleteSomethingMixin(object):
"""Use Mixins to reuse common behavior"""
template_name = 'template-delete.html'
class DeleteCar(DeleteSomethingMixin, DeleteView):
model = Car
class DeleteMake(DeleteSomethingMixin, DeleteView):
model = Make
Just pass your class and form as args to the method then call them in the code.
def edit_object(request, model_cls, model_form, id):
form = model_form(instance = model_cls.objects.get(pk=id)
return render(request, 'template.html', {'form':form})
then just pass in the correct classes and forms in your view methods
def edit_car(request,id):
return edit_object(request, Car, CarForm, id)
each method knows what classes to pass, so you eliminate the if statements.
urls.py
url(r'^car/delete/(?<pk>\d+)/', edit, {'model': Car})
url(r'^make/delete/(?<pk>\d+)/', edit, {'model': Make})
views.py
def edit(request, id, model):
model.objects.get(id=id).delete()
One of the functionalities in a Django project I am writing is sending a newsletter. I have a model, Newsletter and a function, send_newsletter, which I have registered to listen to Newsletter's post_save signal. When the newsletter object is saved via the admin interface, send_newsletter checks if created is True, and if yes it actually sends the mail.
However, it doesn't make much sense to edit a newsletter that has already been sent, for the obvious reasons. Is there a way of making the Newsletter object read-only once it has been saved?
Edit:
I know I can override the save method of the object to raise an error or do nothin if the object existed. However, I don't see the point of doing that. As for the former, I don't know where to catch that error and how to communicate the user the fact that the object wasn't saved. As for the latter, giving the user false feedback (the admin interface saying that the save succeded) doesn't seem like a Good Thing.
What I really want is allow the user to use the Admin interface to write the newsletter and send it, and then browse the newsletters that have already been sent. I would like the admin interface to show the data for sent newsletters in an non-editable input box, without the "Save" button. Alternatively I would like the "Save" button to be inactive.
You can check if it is creation or update in the model's save method:
def save(self, *args, **kwargs):
if self.pk:
raise StandardError('Can\'t modify bla bla bla.')
super(Payment, self).save(*args, **kwargs)
Code above will raise an exception if you try to save an existing object. Objects not previously persisted don't have their primary keys set.
Suggested reading: The Zen of Admin in chapter 17 of the Django Book.
Summary: The admin is not designed for what you're trying to do :(
However, the 1.0 version of the book covers only Django 0.96, and good things have happened since.
In Django 1.0, the admin site is more customizable. Since I haven't customized admin myself, I'll have to guess based on the docs, but I'd say overriding the model form is your best bet.
use readonlyadmin in ur amdin.py.List all the fields which u want to make readonly.After creating the object u canot edit them then
use the link
http://www.djangosnippets.org/snippets/937/
copy the file and then import in ur admin.py and used it
What you can easily do, is making all fields readonly:
class MyModelAdmin(ModelAdmin):
form = ...
def get_readonly_fields(self, request, obj=None):
if obj:
return MyModelAdmin.form.Meta.fields
else: # This is an addition
return []
As for making the Save disappear, it would be much easier if
has_change_permission returning False wouldnt disable even displaying the form
the code snippet responsible for rendering the admin form controls (the admin_modify.submit_row templatetag), wouldnt use show_save=True unconditionally.
Anyways, one way of making that guy not to be rendered :
Create an alternate version of has_change_permission, with proper logic:
class NoSaveModelAdminMixin(object):
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
response = super(NoSaveModelAdmin, self).render_change_form(request, context, add, change,form_url, obj)
response.context_data["has_change_permission"] = self.has_real_change_permission(request, obj)
def has_real_change_permission(self, request, obj):
return obj==None
def change_view(self, request, object_id, extra_context=None):
obj = self.get_object(request, unquote(object_id))
if not self.has_real_change_permission(request, obj) and request.method == 'POST':
raise PermissionDenied
return super(NoSaveModelAdmin, self).change_view(request, object_id, extra_context=extra_context)
Override the submit_row templatetag similar to this:
#admin_modify.register.inclusion_tag('admin/submit_line.html', takes_context=True)
def submit_row(context):
...
'show_save': context['has_change_permission']
...
admin_modify.submit_row = submit_row