I want to create a streaming API(server sent events) for alerts i.e new added alerts should be sent to open connections as server sent events. For that, i am using StreamingHttpResponse. Header is set to text/event-stream as it should be for SSEs. I have a model that handles alerts in DB. I have written a signals.py file that defines signal for post_save for Alert model and for post_save event on Alert model, I return StreamingHttpResponse with newly added alert. When I got the API first time, I get response with all alerts but not getting new added alert/s when ever a new alert is added to DB. What am I doing wrong?
monitoring/signals.py
#receiver(post_save, sender=Alert)
def send_alert(sender, instance, **kwargs):
qs_json = serializers.serialize('json', instance)
return StreamingHttpResponse(qs_json, content_type='text/event-stream')
monitoring/views.py
# just importing defined signal receiver here
from .signals import send_alert
# endpoint view function
def get_alerts(self):
queryset = Alert.objects.all().order_by('-date')
resolved = self.request.query_params.get('resolved')
server = self.request.query_params.get('server')
source = self.request.query_params.get('source')
if source is not None:
queryset = queryset.filter(source=source)
if resolved is not None:
queryset = queryset.filter(resolved=resolved)
if server is not None:
queryset = queryset.filter(server=server)
return HttpResponse(queryset)
Even when I hit the .save method on DB myself on Django shell, signal is not triggered.
>>> from monitoring.models import Alert
>>> a=Alert(container="something")
>>> s.save()
Related
I am learning and working on particular project, where I am allowing few users in the same model (initiator, reviewer and approver) to update model by using model.forms during the different state (initiation (draft), review and approval).
I have no issue on initiation stage as the originator is the same user who does model update. problem which I am struggling appears to happen during review and approval stages which makes my model updated however originator name is replaced by another user if I call:
def update_eq(request, pk):
___rest of the code___
if eq.eq_status == 'Draft':
form = EqForm(request.POST or None, instance=eq)
if form.is_valid():
eq.initiator = request.user
form.save()
___rest of the code___
[Solution] Based on Willem's comment my updated view...
elif eq.eq_status == 'Review':
form = EqForm(request.POST or None, instance=eq)
if form.is_valid():
# eq.initiator = request.user
form.save()
___rest of the code___
However, once I change the state i.e. move to Review or Approve stage and making same update i.e. calling to save form, I am getting the initiator field updated with 'None' value.
Is there any way I allow non-initiator to update the model, but keep model original initiator as current initiator still?
You are editing the Eq instance, so that means the instance is already created and thus the initiator does not need to be set/updated. You thus can remove the eq.initiator = request.user line(s) in your view:
def update_eq(request, pk):
# rest of the code …
if eq.eq_status == 'Draft':
form = EqForm(request.POST or None, instance=eq)
if form.is_valid():
# no eq.initiator = request.user
form.save()
return redirect('name-of-some-view')
# …
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.
I'm intern and work on a project where I develop DRF API that need to interact with mobile app written by my colleague with Ionic framework.
We are creating new user. My view method is following:
class NewUser(generics.CreateAPIView):
model = User
permission_classes = [permissions.AllowAny]
serializer_class = NewUserSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
token, created = Token.objects.get_or_create(user=serializer.instance)
return Response({'token': token.key}, status=status.HTTP_201_CREATED, headers=headers)
When someone wants to create new user via POST request if user name has't been taken yet, then API return 201 status code and token in JSON, if user name already taken it returns 400 status and error message in JSON. MY colleague request me to change status message to 200 when he tries to create username with name that already exist. He says that he can't consume the ERROR response.
His code looks like:
$http.post(url,{
username:$scope.tel,
password:$scope.passwd
}).success(function(data){
alert(data);
$ionicLoading.hide();
console.log(data);
})
Question:
1) Should I tweak my API to send 200 status instead of more logical 400 for 'user already register' error?
I tried to change my code, But i couldn't find the method to override in CreateAPIView/ModelSerializer of DRF. I ended up rewriting my view class to method:
#api_view(['POST'])
def newUser(request):
"""
Saves a new user on the database
"""
if request.method == 'POST':
serializer = NewUserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
token, created = Token.objects.get_or_create(user=serializer.instance)
return Response({'token': token.key}, status=status.HTTP_201_CREATED, headers=serializer.data)
else:
return Response(serializer.errors, status=status.HTTP_200_OK)
Question:
2) If I want to change behaviorof API and responce, which method should I override
3) I'm new to Django and still don't qiute know where we should use generic views VS. #.... methods
200 vs 400 in this case is mostly preference. 400 means "Bad Request". This is generally more correct for a incorrectly formatted request, rather than one that doesn't meet some condition.
200 is just as appropriate it conveys the correct information:
Your request was valid, but I didn't create a new record.
As to how to do the override. The shortest path is to override the CreateAPIView.create and change the response code used. You should also avoid repeating the default behavior of CreateAPIView by calling super.
class CreateUserView(generics.CreateAPIView):
model = User
permission_classes = [permissions.AllowAny]
serializer_class = NewUserSerializer
def create(self, request, *args, **kwargs):
response = super(CreateUserView, self).create(request, *args, **kwargs)
token, created = Token.objects.get_or_create(user=serializer.instance)
response.status = status.HTTP_200_OK
response.data = {'token': token.key}
return response
Personally, I would have also crafted my NewUserSerializer to have a token field and handle the token so I didn't have to do that work in the View. It doesn't belong in a View.
Save and deletion hooks:
The following methods are provided by the mixin classes, and provide easy overriding of the object save or deletion behavior.
perform_create(self, serializer) - Called by CreateModelMixin when
saving a new object instance. perform_update(self, serializer) -
Called by UpdateModelMixin when saving an existing object instance.
perform_destroy(self, instance) - Called by DestroyModelMixin when
deleting an object instance.
These hooks are particularly useful for setting attributes that are implicit in the request, but are not part of the request data. For instance, you might set an attribute on the object based on the request user, or based on a URL keyword argument.
https://www.django-rest-framework.org/api-guide/generic-views/#methods
class CourseOrder(generics.CreateAPIView):
serializer_class = serializers.OrderCoursesSerializer
permission_classes = [permissions.AllowAny]
# hook before creating
def perform_create(self, serializer):
# print(serializer['name'].value)
# save post data
serializer.save()
try:
subject, from_email, to = 'New order', 'zelenchyks#gmail.com', 'zelenchyks#gmail.com'
text_content = 'New order'
html_content = '''
<p>client name: %s </p>
<p>client phone: %s </p>
'''
% (serializer['name'].value, serializer['mobile'].value)
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
msg.attach_alternative(html_content, "text/html")
msg.send()
except Warning:
print('Huston we have a problems with smtp')
The Python source code for Djangular Demos gives examples of how to process the post request from a form that creates a Django object instance. But they don't show how to process the post request from a form which updates an existing object instance.
The code for updating an object seems rather complicated: my code is missing something crucial. Using my code I always get a form validation error: Object with this Name already exists.
I am using the Django generic UpdateView class and my model has a unique field called name.
My code:
from django.views.generic.edit import UpdateView
class MyForm(NgModelFormMixin, Bootstrap3FormMixin, NgModelForm):
scope_prefix='form_data'
form_name = 'my_form'
class Meta:
model = models.MyModel
fields = ['name','person']
class MyModelUpdate(UpdateView):
model = models.MyModel
form_class = MyForm
def post(self, request, **kwargs):
if request.is_ajax():
return self.ajax(request, **kwargs)
return super(MyModelUpdate, self).post(request, **kwargs)
# from the djangular combined_validation example
def ajax(self, request, **kwargs):
# tbd: need update-specific logic here: pass in instance
# parameter (object) or set it from pk. Base class post
# methods use self.get_object()
form = self.form_class(data=json.loads(request.body))
return JsonResponse({'errors': form.errors,
'success_url': force_text(self.success_url)})
What code do I need to get Django to load the instance identified by the pk argument and attach it to the form. That would be the default behavior when the request data comes from POST rather than ajax?
After trial and error experimentation I came up with the following new implementation for the view's ajax method. It passes my tests but feels clunky.
def ajax(self, request, **kwargs):
form = self.form_class(data=json.loads(request.body),
instance=self.get_object())
try:
form.save()
except:
# error is in form.errors
pass
return JsonResponse({'errors': form.errors,
'success_url': force_text(self.success_url)})
In AdminSite, when adding permissions just after I received a post_save signal from a class Groupe which inherit from django.contrib.auth.models.Group:
def create_groupe(sender, instance, created, **kwargs):
if created:
from django.db.models import Q
if instance.role == 'OPERATOR':
grp = Groupe.objects.get(name=instance)
content_type = ContentType.objects.get_for_model(FileDeposit)
permissions = Permission.objects.filter(Q(content_type=content_type), Q(codename__startswith='view_') | Q(codename__startswith='download_'))
grp.permissions = permissions
signals.post_save.connect(create_groupe, sender=Groupe)
It seems the Group.permission m2m is updated with new permissions and then the Manager of Groupe (or Group) call his clear() method.
I've seen this with another signal change_m2m:
signals.m2m_changed.connect(m2m_group, sender=Groupe.permissions.through)
Where am I wrong ?
I've a model named Conversation with some fields that includes date_created and date_updated as DateTimePropterty with auto_now_add and auto_now.
If I update the model using put() method, date_updated field is getting updated.
But when I use the put_async method, the value in the date_updated field is not updating.
And I also have the test case using Python's unittest.Testcase, there it works fine.
Note: It works when I use put_async().get_result().
Sample model class:
class Conversation(ndb.Model):
participants = ndb.StringProperty(repeated=True)
conversation_name = ndb.StringProperty()
date_created = ndb.DateTimeProperty(required=True, auto_now_add=True)
date_updated = ndb.DateTimeProperty(required=True, auto_now=True)
#staticmethod
def update_conversation_date_by_id(conversation_id):
conversation = Conversation.get_by_id(conversation_id) if conversation_id else None
if conversation is None:
raise CannotFindEntity("Given conversation_id is not found")
else:
conversation.put_async().get
return conversation
If the request handler exits before the NDB put finishes the put might never happen.
class MyRequestHandler(webapp2.RequestHandler):
def get(self):
acct = Account.get_by_id(users.get_current_user().user_id())
acct.view_counter += 1
future = acct.put_async()
# ...read something else from Datastore...
template.render(...)
future.get_result()
Try adding something like the last line in that codeblock to force it to wait.
In this example, it's a little silly to call future.get_result: the
application never uses the result from NDB. That code is just in there
to make sure that the request handler doesn't exit before the NDB put
finishes; if the request handler exits too early, the put might never
happen. As a convenience, you can decorate the request handler with
#ndb.toplevel. This tells the handler not to exit until its
asynchronous requests have finished. This in turn lets you send off
the request and not worry about the result.
https://developers.google.com/appengine/docs/python/ndb/async
the async methods stop executing when the main thread stops(request handler finishes)... so most likely your code doesnt execute.
this can be prevented by adding #ndb.toplevel to your request handler. then the handler will wait for your async to complete.
https://developers.google.com/appengine/docs/python/ndb/async
async doesnt let you run code when the request handler is completed. async lets you run other (async) commands while waiting for async commands on the same request handler thread :)