Delete() an instance - python

In my view:
def delete_payment(request, id):
thePayment = Payment.objects.filter(id=id)
thePayment.delete()
return HttpResponseRedirect('/invoices/open/')
In my model:
def delete(self, *args, **kwargs):
raise Exception('foo')
super(Payment, self).delete(*args, **kwargs)
I'm finding that the exception doesn't get raised unless I delete the instance from within the admin view. That is, I can't get delete() to be called properly if I use my own view.

Manager.filter() returns a QuerySet, not a Model. QuerySet.delete() doesn't invoke Model.delete() but rather operates directly on the database.

Related

Changing class attributes in decorator

I'm using django as my framework for some web application.
I implemented a modelview of my own because I have a few querysets and seriazliers in the same view.
For this use, I needed to implement all of the CRUD functions myself:
class Models1AndModel2View(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
model1 = Model1.object.all()
model1_serializer_class = Model1Seriazlizer
model2 = Model2.object.all()
model2_serializer_class = Model2Seriazlizer
def refresh_querysets(func):
def inner(self, *args, **kwargs):
value = func(self, *args, **kwargs)
self.model1 = Model1.object.all()
self.model2 = Model2.object.all()
return value
return inner
#refresh_querysets
def list(self, request, *args, **kwargs):
...
#refresh_querysets
def retrieve(self, pk, request, *args, **kwargs):
...
#refresh_querysets
def update(self, pk, request, *args, **kwargs):
...
#refresh_querysets
def delete(self, pk, request, *args, **kwargs):
...
#refresh_querysets
def create(self, request, *args, **kwargs):
...
Notice that I'm calling the decorator's function before the objects refresh.
I noticed that every attribute set after the function calls is not actually set.
For example - some test of mine:
list all the models - 2 models instances
delete one of them
list them again - still 2 models instances (in model1 + model2)
if you query the model1 and model2 you can see that one of the instances is deleted as expected, but the model1 was not refreshed.
I changed the order on the inner function of the decorator, and it worked as expected.
def refresh_querysets(func):
def inner(self, *args, **kwargs):
self.model1 = Model1.object.all()
self.model2 = Model2.object.all()
return func(self, *args, **kwargs)
return inner
I believe your issue is because the view is temporary.
You save self.model1 on the view which is destroyed and recreated per request.
There is no point updating the self if the object will be destroyed.
Also, you should remember that model1 as set in the class variable is initialized on class creation only, and not on instance creation, meaning it will always contain the original data from when you imported that respective module.
There are 3 options to solve it:
Change the order of the function decorator as you've done, setting self.model1 before running the function, or pass it as a parameter.
Change model1 on __init__, and so the changes will apply on instance creation.
While frowned upon, the class attribute model1 is right now equivalent to a "class global" (and has almost all of a global downsides). You can simply change it like so:
self.__class__.model1 = Model1.object.all()
Keep in mind I do not believe this class attribute should even exist. Should it be immutable, setting it on the instance creation makes more sense.
Either case, I'm not sure if you wish to dump the entire object / DB at every request. I do not know if django smartly caches object.all() (and updates the cache upon modification), but if it doesn't it'll be a major slowdown.

How to display in django admin readonly data from other model?

Why when i call instance of model from method car_name the method return - in Django admin.
#admin.register(Invoice)
class CarProductDataAdmin(admin.ModelAdmin):
form = CarProductDataAdminForm
def car_name(self, obj):
# Call this instance
car = Customer.objects.get(product__customer_id=self.request.user.person.id)
return "car.name" # return string
readonly_fields = ('car_name', )
But when i just return string it is work.
#admin.register(Invoice)
class CarProductDataAdmin(admin.ModelAdmin):
form = CarProductDataAdminForm
def car_name(self, obj):
# Doesn`t Call this instance
# return string
return "name"
readonly_fields = ('car_name', )
From my judgement the django admin seems to fail silently when encountering an exception in a method, which is used as a field.
In your first example the problem seems to be self.request, as the CarProductDataAdmin instance does not have a request attribute. So instead of raising an exception no value is returned to the field resulting in an output "-".
If you need the request you must get it from somewhere and save it in your ModelAdmin for re-use. Maybe by overring the get_form method of the ModelAdmin
class CarProductDataAdmin(admin.ModelAdmin):
# ....
def get_form(self, request, obj=None, change=False, **kwargs):
self.request = request
return super().get_form(request, obj, change, **kwargs)
Then you should be able to use your car_name method as posted in your code.
First noticeable problem in your code is that instead of object attribute value you are returning a string:
return "car.name" # return string
should be:
return f"{car.name}"
or, depending on python version you are using:
return f"{}".format(car.name)
Next one is that calling get on model manager should cause DoesNotExists exception but in some situations it could be suppressed so I'll advice to modify the code to:
try:
car = Customer.objects.get(product__customer_id=self.request.user.person.id)
return f"{car.name}"
except Customer.DoesNotExists:
return "None"
And see if that return what you want or "None"

Pass request to django inherited classes

I am overriding some methods of a popular package, django-activity-stream (I think the package is mostly irrelevant to this question).
from app/urls.py I call TeamJSONActivityFeed
urlpatterns = [
...
url(_(r'^feeds/organization/(?P<organization_id>.+)$'), TeamJSONActivityFeed.as_view(name='organization_stream')),
...
]
TeamJSONactivityFeed then calls 'pass', which I am not too familiar with, and inherits from two other classes, OrganizationStreamMixin and JSONActivityFeed.
from rest_framework.authentication import TokenAuthentication
class TeamJSONActivityFeed(OrganizationStreamMixin, JSONActivityFeed):
"""
JSON feed of Activity for a custom stream. self.name should be the name of the custom stream as defined in the Manager
and arguments may be passed either in the url or when calling as_view(...)
"""
authentication_classes = (TokenAuthentication,)
pass
My issue is that I cannot seem to access/pass the request object in/to these inherited classes. How would I go about passing this in? Right now, self.request.user and request.user are AnonymousUser objects.
class OrganizationStreamMixin(object):
name = None
def get_object(self,request):
# this is printing Anonymous User
pprint(str(self.request.user))
pprint(str(request.user))
return
def get_stream(self):
return getattr(Action.objects, self.name)
def items(self, request, *args, **kwargs):
return self.get_stream()(*args[1:], **kwargs)
class JSONActivityFeed(AbstractActivityStream, View):
"""
Feed that generates feeds compatible with the v1.0 JSON Activity Stream spec
"""
def dispatch(self, request, *args, **kwargs):
for i, v in kwargs.items():
print (" ", i, ": ", v)
return HttpResponse(self.serialize(request, *args, **kwargs),
content_type='application/json')
def serialize(self, request, *args, **kwargs):
pprint(str(self.request.user))
items = self.items(request, *args, **kwargs)
return json.dumps({
'totalItems': len(items),
'items': [self.format(action) for action in items]
})
Note: I am a bit of a django/python noob, but I am sure I am calling this properly from the front end. Similar requests have access to the request user.
I think there's a bit of confusion. You do have access to the request object otherwise it would raise an error for trying to access .user on None. If you're concerned about it being an AnonymousUser instance, then authenticate before accessing that view. If you need to prevent AnonymousUser instances from being able to access that view, then wrap the view with the login_required decorator.
Edit
You're overriding the dispatch method without calling super. That could be the problem.

Use #staticmethod in Django Rest Framework endpoint method?

Should I be defining post, get, etc methods of a Django Rest Framework APIView as static?
class HomeView(APIView):
def get(request):
etc...
or
class HomeView(APIView):
#staticmethod
def get(request):
etc...
What are the pros/cons of each way?
Thank you
DRF does not declare get and post as static methods and neither should you. Here's how the defaults are configured in DRF generics.
It is common in DRF to reference instance methods such as self.get_object and self.get_serializer from within get and post.
class CreateAPIView(mixins.CreateModelMixin,
GenericAPIView):
"""
Concrete view for creating a model instance.
"""
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
class RetrieveAPIView(mixins.RetrieveModelMixin,
GenericAPIView):
"""
Concrete view for retrieving a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
class DestroyAPIView(mixins.DestroyModelMixin,
GenericAPIView):
"""
Concrete view for deleting a model instance.
"""
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
class UpdateAPIView(mixins.UpdateModelMixin,
GenericAPIView):
"""
Concrete view for updating a model instance.
"""
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
As DRF document, You seems do not need to add #staticmethod decorator
DRF won't function correctly without passing ' self '.
PyCharm does mark these methods as 'maybe static', this is false!
Making them static breaks the calling of the method, since the caller method provides the 'self', and your method does not except it. Essentially your still get the self aliased as request, but your other args are ignored. It is possible that this happens silently, that makes it a fairly nasty bug to find :(.
In terms of performance a 'static' or regular function based view should be slightly faster, since you avoid the creation of the instance, but the difference is probably so small that it is hardly noticeable.

Django - how to use kwargs

I'm overriding the .save() method of a django model and I'm trying to pass an extra argument when saving:
View:
def form_valid(self, form):
response = super(DeliveryCreateView, self).form_valid(form)
self.object.save(owner=self.request.user)
return response
In the .save()
def save(self, *args, **kwargs):
owner = kwargs.pop('owner', None)
My problem is that owner always comes empty.
What am I doing wrong?
Assuming this is a standard create or update view, the superclass form_valid will already be calling the model save method, via the form. You'll either need to deal with that situation, or don't call super.

Categories