Django adding an extra unmanaged field to instance in post_save - python

I'm trying to perform a HTTP request in my post_save and in case of a failure to connect to the service I want to send a message back to the user via the response from the view. My post_save looks something like this:
def test_post_save(instance, created, **dummy):
if created:
try:
success = request_function(instance.item.id)
if success:
log.debug('Request succeeded')
except requests.exceptions.ConnectionError:
instance.stop = instance.start
instance.save()
log.debug('Request failed')
The above code works just fine, What I want to do is to add a field to the instance or in someway return a message back to the view's create function which utilizes serializer.data that the request failed.
I was hoping to achieve this without adding an extra column on the model's table. I've read about unmanaged models but I couldn't find any reference to an unmanaged field. Is there any other way for me to achieve this?
My end goal is to basically send back a message in the response to the client letting them know that the connection failed.

Related

How to add a function after a record inserted in database using django admin?

I want to execute a function after a record inserted to database using django-admin panel .
I have a product table , i want to send notification to users when a record inserted in database by django admin panel . i know how to send notification to users , but i dont know where to put my code .
any suggestion will be helpfull .
How can i execute a function to notify user after my record inserted ?
here is my code to execute after record inserted :
from fcm_django.models import FCMDevice
device = FCMDevice.objects.all()
device.send_message(title="Title", body="Message", icon=..., data={"test": "test"})
i searched alot but not found anything usefull .
thanks to this great communtiy .
You can modify save method on your product model
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
device = FCMDevice.objects.all()
device.send_message(title="Title", body="Message", icon=..., data={"test": "test"})
Well, it will send the notification every time the instance is saved (not only when it was added in django admin panel).
You need to use the Django model post_save signals to achieve this. This signal receiver can be placed in the same place where the model is
class FCMDevice(models.Model):
...
#receiver(post_save, sender=FCMDevice)
def notify_users(sender, instance, **kwargs):
# your logic goes here
# instance is referred to currently inserted row
pass
You might wanna check post_save signal. From the docs:
Like pre_save, but sent at the end of the save() method.
url: https://docs.djangoproject.com/en/2.1/ref/signals/#post-save
django-version: 1.7+

Django pre_save signal and ModelAdmin custom error message

I have a model whose pre_save() signal is connected to a remove service (json, REST, etc.) in the following way:
before saving locally, query the remote service, asking for remote insertion
remote service does its things, the main being checking that a relevant entry does not already exist.
On success (HTTP 201), all is good, the local model uses the remote service response to populate the db.
on failure, the service returns an HTTP 400 (the status code is debatable but that is for a different question on SO :-) )
The error response is in the following form:
{'local_model_field': [u'This element already exists']}
The local model pre_save signal then raises a ValidationError:
raise ValidationError(json_response['local_model_field'][0])
This works nicely.
Now, on the django admin, when I try to simulate the remote insertion of an already-existing object, I get a 500 page, which is fine but not ideal.
Is there any way to have the pre_save() error bubble all the way up to the ModelAdmin and be displayed as a standard error message, populated with the relevant content?
I have tried the following but to no avail:
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
"""
trying to display the right message when pre_save() fails on model save() method (after asking CC)
"""
try:
return super(ObjectAdmin, self).changeform_view(request, object_id, form_url, extra_context)
except IntegrityError as e:
self.message_user(request, e, level=messages.ERROR)
return HttpResponseRedirect(form_url)
Is a ValidationError the right thing to do? Knowing that the pre_save() must lock out any chance of ending with with duplicates both locally and remotely. The main reason is that the local/remote object creation can be made form the admin but also from other website instances/types (front-end, end-user facing, for example).
Thanks
Not sure if this is still relevant.
But the way I solved this is by creating a ModelForm:
class AuthorAdminForm(forms.ModelForm):
def clean(self):
# or some api calls here
if self.instance.id > 4:
self.instance.name = "4+"
else:
ValidationError("Id is bigger that 4")
return super().clean()
And then by adding the form to the admin model:
class AuthorAdmin(admin.ModelAdmin):
form = AuthorAdminForm
This clean() method will ensure that you could add/modify fields before you hit the model save() method and still throw a ValidationError if something goes south like a 404 error when hitting a url

Django: HTTP status 400 response rolls back model save?

I have an API endpoint to trigger a payment request to a third party. The endpoint is called with some data in a POST request and goes something like the code below.
Somehow when the payment request failed (payment.is_successful = False), no data was ever saved to the database, although the debug log showed SQL INSERTs being made, and no error was thrown. The data was just dropped silently.
I've not set the database to use ATOMIC_REQUESTS (I'm using postgreSQL). And I'm using django 1.11.5 and DRF 3.6.3.
I figured that if I changed HTTP_400_BAD_REQUEST to HTTP_200_OK then my data is saved.
Why is this happening (and where is the code responsible for it)?
Is there a way to prevent it from happening (I want my data in the db for this view, no matter what) with some setting in Django/DRF?
I've temporarily set the return code to 200, but it feels wrong as the request actually failed. What code would make more sense, that doesn't cause the data to disappear?
view code
ser = MySerializer(data=request.data)
if ser.is_valid():
payment = ser.save()
else:
# do some non database stuff
return Response(result, status=status.HTTP_400_BAD_REQUEST)
if payment.is_successful:
# some more processing
return Response({'success': True}, status=status.HTTP_201_CREATED)
else:
return Response({'success': False}, status=status.HTTP_400_BAD_REQUEST) ## THIS LINE ##
serializer code
class MySerializer(serializers.ModelSerializer):
def create(...):
# call payment provider and process response
payment = MyPayment()
payment.save() # contains response from the provider that I always want to keep
return payment
DRF doesn't rollbacks transactions unless it's an exceptions and it meets some criteria.
This looks like the issue is somewhere outside Django REST framework.
Double check your middlewares for some may apply a rollback in case of a non 200 error.

Send user data in python requests module

I am unable to send the user details along with requests module i had to hard code the user details in the data payload to identify the user.
full_url = ''.join(['http://', get_current_site(request).domain, '/am/reply'])
data = {
'agent_type':'trigger',
'input':platform,
'userid':request.user.id ####==>> had to send userid like this
}
a = requests.get(full_url,params=data)
Is there way to send all general request data using requests.?
And moreover the requests url the destination view i have implemented
def index(request):
if not request.user.is_authenticated:
return HttpResponseRedirect(reverse('login'))
And request.user.id is none when url is reached through requests module
In general how should i validate a request when using requests module
Django uses request and response objects to pass state through the system.
When a page is requested, Django creates an HttpRequest object that contains metadata about the request. Then Django loads the appropriate view, passing the HttpRequest as the first argument to the view function. Each view is responsible for returning an HttpResponse object.
Some of the middleware included in Django’s contrib apps set attributes on the request. If you don’t see the attribute on a request, be sure the appropriate middleware class like authenticationmiddleware,sessionmiddleware.
Following piece of code will give the user.id if and only if the user is authenticated.
def myview(request):
if request.user.is_authenticated:
print request.user.id
else:
... # Do something else.
https://docs.djangoproject.com/en/1.10/ref/request-response/
If I understood your question correctly, You are getting request in one view, and then making a call to other view using requests module. It that case the request object in index view will be totally different because that request was sent from your server where application works, not from user. You can only get data in index view using request.GET.get("userid") and so on. And then if you will need user info, just fetch it again from database using userid. Passing request object to other view using requests library is not possible.

How to access REMOTE_ADDR in a model's save() method

I have a model (UserProfile) with which I want to record a user's IP address when new user accounts are created.
I've begin the process of overriding the Django model's save() method, but I am totally uncertain how to properly access HttpRequest attributes from within a model. Can any of you guys help? Google was unable to provide a specific answer for me.
You can get to the request from within the admin, but you can't do it in general code. If you need it then you'll have to assign it manually in the view.
You always need to pass on request information from the view to your save-method.
Consider that saving an instance doesn't always have to be invoked from a http request (for a simple example think of calling save in the python shell).
If you need to access the request object within the admin while saving, you can override it's save_model method:
def save_model(self, request, obj, form, change):
# do something with obj and request here....
obj.save()
But otherwise you always have to pass it on from the view:
def my_view(request):
obj = MyClass(ip_address = request.META['REMOTE_ADDR'])
Or to make this easier to re-use, make a method on the model like:
def foo(self, request):
self.ip_address = request.META['REMOTE_ADDR']
self..... = request.....
and call it from your view with obj.foo(request).

Categories

Resources