Hi I've been wondering how to rollback a saved object on a following method when one method failed.
forms.py
# somewhere class forms
def handle(self, view, request, *args, **kwargs):
# assuming i get an object successfully.
application = models.Application.objects.first()
# do something in its attrs and save
application.name = 'test'
application.save()
# application is fix and send via django.core.mail EmailMessage
self.send_mail(application)
return True
now when I had a good connection everything will work fine. But when it does not it will save the application but will not send the email. Resend is not an option since its a one time send supposedly. Now my idea to rollback the application and throw exception something like "Action Failed. Maybe poor connection. Please try again".
try:
application.save()
self.send_mail(application)
except ConnectionError?:
# rollback ?
raise Exception(msg) ?
Any help and better way to handle it? Thanks for the help and sorry for the trouble.
Related
I would like to store the last messages to get a chance for the user to check it again.
How can I save it, after show? before show? Do I need to implement a db model?
you can use redis,
Redis might come in handy.
I found a nice solution, without using external library. Just with my lovely python!
First I create a function to store the messages into a database:
def save_messages(message):
db.objects.add(ManyToMany.object.create(message))
return redirect('message_saved')
and then I put the function to run anytime a message is being sent into contrib messages itself
MYPROJECT\venv\Lib\site-packages\django\contrib\messages\api.py
def add_message(request, level, message, extra_tags='', fail_silently=False):
"""
Attempt to add a message to the request using the 'messages' app.
"""
try:
messages = request._messages
except AttributeError:
if not hasattr(request, 'META'):
raise TypeError(
"add_message() argument must be an HttpRequest object, not "
"'%s'." % request.__class__.__name__
)
if not fail_silently:
raise MessageFailure(
'You cannot add messages without installing '
'django.contrib.messages.middleware.MessageMiddleware'
)
else:
from WALLAWALLA import save_messages
save_messages(message)
return messages.add(level, message, extra_tags)
and this really works great
I have a OneToOneField between my UserTrust model and django.contrib.auth.models.User that I would like to create whenever a new User registers. I thought on creating the UserTrust instance using the user_signed_up signal.
I have the following code in my AppConfig
def ready(self):
# importing model classes
from .models import UserTrust
#receiver(user_signed_up)
def create_usertrust(sender, **kwargs):
u = kwargs['user']
UserTrust.objects.create(user=u)
... and this is in my pytest test
#pytest.fixture
def test_password():
return 'strong-test-pass'
#pytest.fixture
def create_user(db, django_user_model, test_password):
def make_user(**kwargs):
kwargs['password'] = test_password
if 'username' not in kwargs:
kwargs['username'] = str(uuid.uuid4())
return django_user_model.objects.create_user(**kwargs)
return make_user
#pytest.mark.django_db
def test_my_user(create_user):
user = create_user()
assert user.usertrust.available == 1000
Still, the test fails with
django.contrib.auth.models.User.usertrust.RelatedObjectDoesNotExist: User has no usertrust.
What did I miss?
The problem with you creating the user via the django_user_model is that it doesn't actually pass through the allauth code that actually sends that signal. You've got two options:
Use a client (since I'm assuming you're using pytest-django) and fill out a registration via the allauth provided link for registering new users. That way, a signal is sent, and you can assert attributes and model instances like that.
You can simply ignore the signal and unittest the function itself. That's it. You put your trust in that single registration view not changing at all and put your trust in the package that the API will not change. I can still recommend this option, but you've been warned.
You can send the signal yourself. Not recommended in case allauth's API changes, but you could just import the signal from allauth and send it like this: user_signed_up.send(sender=self.__class__, toppings=toppings, size=size) where user_signed_up is the signal. Ref the docs: https://docs.djangoproject.com/en/dev/topics/signals/#sending-signals
Again, definitely recommend the first one in case of API changes. I can also recommend the second option just because allauth is pretty reputable and you know what going to happen without too huge of an package change, but you never know.
I use the django-restful framework and I want to add user info to Sentry's report when an error happened in the ModelViewSet.
I find this doc of Sentry:
https://docs.sentry.io/enriching-error-data/context/?_ga=1.219964441.1220115692.1472094716%3F_ga&platform=python#capturing-the-user
It gives some code as follows:
from sentry_sdk import configure_scope
with configure_scope() as scope:
scope.user = {"email": "john.doe#example.com"}
But I can not figure out how to properly use it. I think there exists a better way than the following:
#list_route()
def fun_xxx(self, request, *args, **kwargs):
user = request.user
with configure_scope() as scope:
scope.user = {"id": user.id,......}
...some code may cause an error...
return Response({...})
Can anyone give me some suggestions? :)
As mentioned in the comments, the Django integration will attach this particular data automatically.
As for the question on how to generally add data in a Django app, you are basically looking for something to run before each view. A Django middleware suits this:
def sentry_middleware(get_response):
def middleware(request):
with configure_scope() as scope:
...
response = get_response(request)
return response
return middleware
https://docs.djangoproject.com/en/2.2/topics/http/middleware/
As mentioned here: https://docs.sentry.io/platforms/python/guides/django/#configure
Set:
# If you wish to associate users to errors (assuming you are using
# django.contrib.auth) you may enable sending PII data.
send_default_pii=True,
while configuring the sentry in Django.
PII: Personal Identifiable Information
P.S. Don't forget to include django.contrib.auth in your installed_apps.
I have a profile page like so: http://i.stack.imgur.com/Rx4kg.png . In management I would like a option "Notify by mail" that would control my send_email functions in every application I want. As example I'm using django-messages and it sends private messages aswell as emails when you send a message. I would like for the user to be able to specify if he wants emails aswell when he gets a message.
messages/utils.py
def new_message_email(sender, instance, signal,
subject_prefix=_(u'New Message: %(subject)s'),
template_name="messages/new_message.html",
default_protocol=None,
*args, **kwargs):
"""
This function sends an email and is called via Django's signal framework.
Optional arguments:
``template_name``: the template to use
``subject_prefix``: prefix for the email subject.
``default_protocol``: default protocol in site URL passed to template
"""
if default_protocol is None:
default_protocol = getattr(settings, 'DEFAULT_HTTP_PROTOCOL', 'http')
if 'created' in kwargs and kwargs['created']:
try:
current_domain = Site.objects.get_current().domain
subject = subject_prefix % {'subject': instance.subject}
message = render_to_string(template_name, {
'site_url': '%s://%s' % (default_protocol, current_domain),
'message': instance,
})
if instance.recipient.email != "":
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
[instance.recipient.email,])
except Exception, e:
#print e
pass #fail silently
Apparently instance.recipient.email is the email for the recipient user. So my questions are: How do I go about creating an option in my profile management that can be used in my new_message_email to check if the user wants emails or not? My own thoughts are that I need to save a value in the database for the user and then check for that value in new_message_email function. How I do that isn't clear though. Do I create a new function in my userprofile/views.py and class in userprofile/forms.py? And have my userprofile/overview.html template change them? Some specifics and thoughts if this is the right approach would help alot!
You probably want to start off by creating a user profile so that you have a good way to store weather or not the user wants these emails sent to them. This is done using the AUTH_PROFILE_MODULE setting in your settings.py.
Once you have the data stored, you should be able to access it from instance.recipient (assuming that instance.recipient is a User object). So you could change your code to:
if instance.recipient.get_profile().wants_emails and instance.recipient.email != "":
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
[instance.recipient.email,])
Done and done.
In my Django application I want to keep track of whether a response has been sent to the client successfully. I am well aware that there is no "watertight" way in a connectionless protocol like HTTP to ensure the client has received (and displayed) a response, so this will not be mission-critical functionality, but still I want to do this at the latest possible time. The response will not be HTML so any callbacks from the client (using Javascript or IMG tags etc.) are not possible.
The "latest" hook I can find would be adding a custom middleware implementing process_response at the first position of the middleware list, but to my understanding this is executed before the actual response is constructed and sent to the client. Are there any hooks/events in Django to execute code after the response has been sent successfully?
The method I am going for at the moment uses a subclass of HttpResponse:
from django.template import loader
from django.http import HttpResponse
# use custom response class to override HttpResponse.close()
class LogSuccessResponse(HttpResponse):
def close(self):
super(LogSuccessResponse, self).close()
# do whatever you want, this is the last codepoint in request handling
if self.status_code == 200:
print('HttpResponse successful: %s' % self.status_code)
# this would be the view definition
def logging_view(request):
response = LogSuccessResponse('Hello World', mimetype='text/plain')
return response
By reading the Django code I am very much convinced that HttpResponse.close() is the latest point to inject code into the request handling. I am not sure if there really are error cases that are handled better by this method compared to the ones mentioned above, so I am leaving the question open for now.
The reasons I prefer this approach to the others mentioned in lazerscience's answer are that it can be set up in the view alone and does not require middleware to be installed. Using the request_finished signal, on the other hand, wouldn't allow me to access the response object.
If you need to do this a lot, a useful trick is to have a special response class like:
class ResponseThen(Response):
def __init__(self, data, then_callback, **kwargs):
super().__init__(data, **kwargs)
self.then_callback = then_callback
def close(self):
super().close()
self.then_callback()
def some_view(request):
# ...code to run before response is returned to client
def do_after():
# ...code to run *after* response is returned to client
return ResponseThen(some_data, do_after, status=status.HTTP_200_OK)
...helps if you want a quick/hacky "fire and forget" solution without bothering to integrate a proper task queue or split off a separate microservice from your app.
I suppose when talking about middleware you are thinking about the middleware's process_request method, but there's also a process_response method that is called when the HttpResponse object is returned. I guess that will be the latest moment where you can find a hook that you can use.
Furthermore there's also a request_finished signal being fired.
I modified Florian Ledermann's idea a little bit... So someone can just use the httpresponse function normally, but allows for them to define a function and bind it to that specific httpresponse.
old_response_close = HttpResponse.close
HttpResponse.func = None
def new_response_close(self):
old_response_close(self)
if self.func is not None:
self.func()
HttpResponse.close = new_response_close
It can be used via:
def myview():
def myfunc():
print("stuff to do")
resp = HttpResponse(status=200)
resp.func = myfunc
return resp
I was looking for a way to send a response, then execute some time consuming code after... but if I can get a background (most likely a celery) task to run, then it will have rendered this useless to me. I will just kick off the background task before the return statement. It should be asynchronous, so the response will be returned before the code is finished executing.
---EDIT---
I finally got celery to work with aws sqs. I basically posted a "how to". Check out my answer on this post:
Cannot start Celery Worker (Kombu.asynchronous.timer)
I found a filthy trick to do this by accessing a protected member in HttpResponse.
def some_view(request):
# ...code to run before response is returned to client
def do_after():
# ...code to run *after* response is returned to client
response = HttpResponse()
response._resource_closers.append(do_after)
return response
It works in Django 3.0.6 , check the "close" function in the prototype of HttpResponse.
def close(self):
for closer in self._resource_closers:
try:
closer()
except Exception:
pass
# Free resources that were still referenced.
self._resource_closers.clear()
self.closed = True
signals.request_finished.send(sender=self._handler_class)