django rest framework: getting two post request in the back end - django

I have an Angular app posting registration message to my django back-end, however, when I click on register button on the page, I got tow post request in the django logging, like this
[17/Apr/2018 22:13:47] "OPTIONS /user/register HTTP/1.1" 200 0
[17/Apr/2018 22:13:47] "POST /user/register HTTP/1.1" 500 27
[17/Apr/2018 22:13:47] "POST /user/register HTTP/1.1" 201 91
Chrome dev tool - Network
It is just annoying when testing locally, but when I deploy this on a ubuntu server(with uwsgi and nginx), the back-end seems to crash. I am not sure if this is the problem, I am just checking every possibility that I can think of.
BTW: I am using sqlite, it is because of the transaction?
Angular registration.component.js
import { Component, OnInit, AfterViewInit, ViewChild } from '#angular/core';
import { NgForm } from "#angular/forms";
import { HttpClient } from "#angular/common/http";
#Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.scss']
})
export class RegisterComponent implements OnInit, AfterViewInit {
formData = {} as any;
isHide: boolean;
constructor(
private http: HttpClient,
) {
this.isHide = true;
}
formErrors = {
'email': '',
'userName': '',
'password1': '',
'password2': '',
'phone': ''
};
validationMessages = {
'email': {
'required': '邮箱必须填写.',
'pattern': '邮箱格式不对',
},
'userName': {
'required': '用户名必填.',
'minlength': '用户名太短',
},
'password1': {
'required': '请输入密码',
'minlength': '密码太短',
},
'password2': {
'required': '请重复输入密码',
'minlength': '密码太短',
},
'phone': {
'required': '手机号必须填写.',
'pattern': '手机号格式不对',
},
};
#ViewChild('registerForm') registerForm: NgForm;
ngAfterViewInit(): void {
this.registerForm.valueChanges.subscribe(data => this.onValueChanged(data));
}
onValueChanged(data) {
if (this.formErrors) {
for (const field in this.formErrors) {
this.formErrors[field] = '';
const control = this.registerForm.form.get(field);
if (control && control.dirty && !control.valid) {
const messages = this.validationMessages[field];
if (control.errors) {
for (const key in control.errors) {
this.formErrors[field] += messages[key] + '';
}
}
}
}
}
}
doJumpIndex() {
console.log("zhuye");
}
doJumpLogin() {
console.log("login");
}
doSubmit(obj: any) {
if (!this.registerForm.valid) {
this.onValueChanged(obj);
return;
}
let url = 'http://localhost:8000/user/register';
this.http.post(url, obj).subscribe(
data => {
console.log(data);
if (true) {
this.isHide = false;
}
},
err => {
console.log(err);
});
}
ngOnInit() {
}
}
The following code was adopted from https://github.com/iboto/django-rest-framework-user-registration
my UserRegistrationAPIView
class UserRegistrationAPIView(generics.CreateAPIView):
permission_classes = (permissions.AllowAny,)
serializer_class = serializers.UserRegistrationSerializer
queryset = User.objects.all()
UserRegistrationSerializer
class UserRegistrationSerializer(serializers.ModelSerializer):
email = serializers.EmailField(
required=True,
label="Email Address"
)
password1 = serializers.CharField(
required=True,
label="Password",
style={'input_type': 'password'}
)
password2 = serializers.CharField(
required=True,
label="Confirm Password",
style={'input_type': 'password'}
)
invite_code = serializers.CharField(
required=False
)
class Meta(object):
model = User
fields = ['username', 'email', 'password1', 'password2', 'invite_code']
def validate_email(self, value):
if User.objects.filter(email=value).exists():
raise serializers.ValidationError("Email already exists.")
return value
def validate_username(self, value):
if User.objects.filter(username=value).exists():
raise serializers.ValidationError("Username already exists.")
return value
def validate_invite_code(self, value):
data = self.get_initial()
email = data.get('email')
if value:
self.invitation = TeamInvitation.objects.validate_code(email, value)
if not self.invitation:
raise serializers.ValidationError("Invite code is not valid / expired.")
self.team = self.invitation.invited_by.team.last()
return value
def create(self, validated_data):
team = getattr(self, 'team', None)
user_data = {
'username': validated_data.get('username'),
'email': validated_data.get('email'),
'password': validated_data.get('password1')
}
is_active = True if team else False
user = UserProfile.objects.create_user_profile(
data=user_data,
is_active=is_active,
site=get_current_site(self.context['request']),
send_email=True
)
if team:
team.members.add(user)
if hasattr(self, 'invitation'):
TeamInvitation.objects.accept_invitation(self.invitation)
TeamInvitation.objects.decline_pending_invitations(email_ids=[validated_data.get('email')])
return validated_data
urls.py
urlpatterns = [
path('login', views.UserLoginAPIView.as_view(), name='login'),
path('register', views.UserRegistrationAPIView.as_view(), name='register'),
path('profile', views.UserProfileAPIView.as_view(), name='user_profile'),
path('password_reset', views.PasswordResetAPIView.as_view(), name='password_change'),
re_path(r'^verify/(?P<verification_key>.+)/$',
views.UserEmailVerificationAPIView.as_view(),
name='email_verify'),
re_path(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
views.PasswordResetConfirmView.as_view(),
name='password_reset_confirm'),
]

It looks to me like your doSubmit angular function might be firing twice. From the logs, it looks like it's sending a request with incorrect information (the first call is 27 bytes long, the second (correct one) is 91 bytes). This first request is getting a HTTP 500 error (Server side error). i.e. it's causing a bug in the server.
On your local testing server a 500 error won't crash the server as it will just log the error and reload (because that's what testing servers do) but on your proper server it is crashing.
I'd say take a look at the doSubmit function and make sure it's not letting any incorrect values past the first if (!this.registerForm.valid) first (sometimes I find when Angular is first creating the objects functions fire that I wouldn't have expected to, for example). And secondly, I'd add some logging to figure out what part of UserRegistrationAPIView is failing (because that's the first function Django calls and somewhere along the way an uncaught exception is being thrown).
If you check the main servers logs they might tell you why the 500 internal server error is being thrown. (Saved in /var/log/apache2/error.log by default if you're running an apache server).

Related

django : Save Buffer in FileField

I need to save file received from an express(nodejs) server in django file field format
Django==2.0
class DoctorFileUpload(views.APIView):
permission_classes = (AllowAny, )
def post(self, request, *args, **kwargs):
fileFromRequest = request.data['doctorfile']
newFile = dict()
newFile['format'] = fileFromRequest['mimetype']
newFile['doctor'] = request.userprofile.doctoraccount.id
newFile['file'] = ContentFile(
fileFromRequest['buffer'], name=fileFromRequest['originalname'])
doctorfile_srz = DoctorFileSrz(data=newFile)
if doctorfile_srz.is_valid():
doctorfile_srz.save()
return toolsViews.ResponseHandler(
status.HTTP_202_ACCEPTED,
{
'doctorfile': doctorfile_srz.data
}
)
else:
return toolsViews.ResponseHandler(
status.HTTP_400_BAD_REQUEST,
{},
errors=doctorfile_srz.errors
)
I need to save a file in a FileField Django Model, there is the request :
{
doctorfile: {
fieldname: 'file',
originalname: 'Capture d’écran 2022-10-06 à 10.33.56.png',
encoding: '7bit',
mimetype: 'image/png',
buffer: { type: 'Buffer', data: [Array] },
size: 134774
},
category: 'OT'
}
I don't know how to do, i checked documentation but i didn't find the solution...
Do i need to convert to b64 format or other format before saving?
here is the error when i tried the code :
TypeError: a bytes-like object is required, not 'dict'

Get random questions in a quizgame django API

I am trying to recreate game of hacks because there isnt an API to create my own questions , and implement on external site , however I am using django with restful framework for this task. (I am not sure , if this is the right to achieve this). I will do this via server because I dont want people change js and bypass the stuff or even disable js and stop time , and continue with the same question
apiview.py
#api_view(['GET', 'POST'])
def questions_view(request):
if request.method == 'GET':
questions = Question.objects.all()
serializer = QuestionListPageSerializer(questions, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = QuestionListPageSerializer(data=request.data)
if serializer.is_valid():
question = serializer.save()
return Response(QuestionListPageSerializer(question).data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers
class QuestionListPageSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
question_text = serializers.CharField(max_length=200)
pub_date = serializers.DateTimeField()
was_published_recently = serializers.BooleanField(read_only=True) # Serializer is smart enough to understand that was_published_recently is a method on Question
code = serializers.CharField(max_length=200)
def create(self, validated_data):
return Question.objects.create(**validated_data)
def update(self, instance, validated_data):
for key, value in validated_data.items():
setattr(instance, key, value)
instance.save()
return instance
question list my app
HTTP 200 OK
Allow: POST, OPTIONS, GET
Content-Type: application/json
Vary: Accept
[
{
"id": 1,
"question_text": "helllo",
"pub_date": "2020-01-15T02:30:40Z",
"was_published_recently": false,
"code": "int main (int argc, char *argv[])\r\n{\r\n\tchar *val = argv[argc -1];\r\n\tsystem(val);\r\n\treturn(0);\r\n}"
},
{
"id": 2,
"question_text": "What is the meaning of life?",
"pub_date": "2020-01-15T02:30:40Z",
"was_published_recently": false,
"code": "SOME STRING"
}
]
question with answer my app
HTTP 200 OK
Allow: PATCH, OPTIONS, DELETE, GET
Content-Type: application/json
Vary: Accept
{
"id": 1,
"question_text": "helllo",
"pub_date": "2020-01-15T02:30:40Z",
"was_published_recently": false,
"code": "int main (int argc, char *argv[])\r\n{\r\n\tchar *val = argv[argc -1];\r\n\tsystem(val);\r\n\treturn(0);\r\n}",
"choices": [
{
"id": 1,
"choice_text": "11111"
}
]
}
game of hacks external app
{
"_id": "53fb4014e5d4d40400c7fa4f",
"answers": [
"LDAP Injection",
"CGI Reflected XSS",
"Connection String Injection",
"Reflected XSS"
],
"batch_score": 39.89902034664657,
"checkmarx": true,
"from": "Gilad",
"language": "PHP",
"level": "1",
"question": "What vulnerability the following code contains?",
"snippets": [
{
"code": "<?php\n$dn = $_GET['host'];\n$filter=\"(|(sn=$person*)(givenname=$person*))\";\n$justthese = array(\"ou\", \"sn\", \"givenname\", \"mail\");\n$sr=ldap_search($ds, $dn, $dn, $justthese);\n$info = ldap_get_entries($ds, $sr);\necho $info[\"count\"].\" entries returned\n\";?>"
}
],
"tip": ""
}
example
http://www.gameofhacks.com/api/answer
answer
{quest: true, score: 5700}
quest: true
score: 5700
http://www.gameofhacks.com/api/question # make random instead of api/poll/question/{id} ?
You can use python random library in your view:
from random import randint
def random_question(request):
i = randint(0, Question.objects.count() - 1)
question = Question.objects.all()[i] # get question at random position
....

Graphene errors messages

I wonder if it is possible to translate the validation error messages that graphene provides? For example: "Authentication credentials were not provided" as shown in the code example below.
{
"errors": [
{
"message": "Authentication credentials were not provided",
"locations": [
{
"line": 2,
"column": 3
}
]
}
],
"data": {
"viewer": null
}
}
Create a custom error type
import graphene
from graphene_django.utils import camelize
class ErrorType(graphene.Scalar):
#staticmethod
def serialize(errors):
if isinstance(errors, dict):
if errors.get("__all__", False):
errors["non_field_errors"] = errors.pop("__all__")
return camelize(errors)
raise Exception("`errors` should be dict!")
Add it to your mutations
class MyMutation(graphene.Mutation):
# add the custom error type
errors = graphene.Field(ErrorType)
form = SomeForm
#classmethod
def mutate(cls, root, info, **kwargs):
f = cls.form(kwargs)
if f.is_valid():
pass
else:
# pass the form error to your custom error type
return cls(errors=f.errors.get_json_data())
Example
django-graphql-auth uses a similar error type, and it works like this, for example for registration:
mutation {
register(
email:"skywalker#email.com",
username:"skywalker",
password1: "123456",
password2:"123"
) {
success,
errors,
token,
refreshToken
}
}
should return:
{
"data": {
"register": {
"success": false,
"errors": {
"password2": [
{
"message": "The two password fields didn’t match.",
"code": "password_mismatch"
}
]
},
"token": null,
"refreshToken": null
}
}
}
My Django form errors types, for example:
from graphene.utils.str_converters import to_camel_case
class DjangoFormError(graphene.ObjectType):
field = graphene.String()
message = graphene.String()
#classmethod
def list_from_errors_dict(cls: Type[T], django_form_errors: dict) -> List[T]:
return [
cls(field=to_camel_case(field), message=' '.join(messages))
for field, messages in django_form_errors.items()
]
class DjangoFormErrorsByIdx(graphene.ObjectType):
form_idx = graphene.Int()
errors = graphene.List(DjangoFormError)
#classmethod
def list_from_idx_dict(cls: Type[T], errors_by_idx_dict: dict) -> List[T]:
return [
cls(
form_idx=idx,
errors=DjangoFormError.list_from_errors_dict(django_form_errors),
)
for idx, django_form_errors in errors_by_idx_dict.items()
]
# ...
# in mutation
if not django_form.is_valid():
form_errors = DjangoFormError.list_from_errors_dict(
django_form.errors
)

Mock Stripe Methods in Python for testing

So I am trying to mock all the stripe web hooks in the method so that I can write the Unit test for it. I am using the mock library for mocking the stripe methods. Here is the method I am trying to mock:
class AddCardView(APIView):
"""
* Add card for the customer
"""
permission_classes = (
CustomerPermission,
)
def post(self, request, format=None):
name = request.DATA.get('name', None)
cvc = request.DATA.get('cvc', None)
number = request.DATA.get('number', None)
expiry = request.DATA.get('expiry', None)
expiry_month, expiry_year = expiry.split("/")
customer_obj = request.user.contact.business.customer
customer = stripe.Customer.retrieve(customer_obj.stripe_id)
try:
card = customer.sources.create(
source={
"object": "card",
"number": number,
"exp_month": expiry_month,
"exp_year": expiry_year,
"cvc": cvc,
"name": name
}
)
# making it the default card
customer.default_source = card.id
customer.save()
except CardError as ce:
logger.error("Got CardError for customer_id={0}, CardError={1}".format(customer_obj.pk, ce.json_body))
return Response({"success": False, "error": "Failed to add card"})
else:
customer_obj.card_last_4 = card.get('last4')
customer_obj.card_kind = card.get('type', '')
customer_obj.card_fingerprint = card.get('fingerprint')
customer_obj.save()
return Response({"success": True})
This is the method for unit testing:
#mock.patch('stripe.Customer.retrieve')
#mock.patch('stripe.Customer.create')
def test_add_card(self,create_mock,retrieve_mock):
response = {
'default_card': None,
'cards': {
"count": 0,
"data": []
}
}
# save_mock.return_value = response
create_mock.return_value = response
retrieve_mock.return_value = response
self.api_client.client.login(username = self.username, password = self.password)
res = self.api_client.post('/biz/api/auth/card/add')
print res
Now stripe.Customer.retrieve is being mocked properly. But I am not able to mock customer.sources.create. I am really stuck on this.
This is the right way of doing it:
#mock.patch('stripe.Customer.retrieve')
def test_add_card_failure(self, retrieve_mock):
data = {
'name': "shubham",
'cvc': 123,
'number': "4242424242424242",
'expiry': "12/23",
}
e = CardError("Card Error", "", "")
retrieve_mock.return_value.sources.create.return_value = e
self.api_client.client.login(username=self.username, password=self.password)
res = self.api_client.post('/biz/api/auth/card/add', data=data)
self.assertEqual(self.deserialize(res)['success'], False)
Even though the given answer is correct, there is a way more comfortable solution using vcrpy. That is creating a cassette (record) once a given record does not exist yet. When it does, the mocking is done transparently and the record will be replayed. Beautiful.
Having a vanilla pyramid application, using py.test, my test now looks like this:
import vcr
# here we have some FactoryBoy fixtures
from tests.fixtures import PaymentServiceProviderFactory, SSOUserFactory
def test_post_transaction(sqla_session, test_app):
# first we need a PSP and a User existent in the DB
psp = PaymentServiceProviderFactory() # type: PaymentServiceProvider
user = SSOUserFactory()
sqla_session.add(psp, user)
sqla_session.flush()
with vcr.use_cassette('tests/casettes/tests.checkout.services.transaction_test.test_post_transaction.yaml'):
# with that PSP we create a new PSPTransaction ...
res = test_app.post(url='/psps/%s/transaction' % psp.id,
params={
'token': '4711',
'amount': '12.44',
'currency': 'EUR',
})
assert 201 == res.status_code
assert 'id' in res.json_body
IMO, the following method is better than the rest of the answers
import unittest
import stripe
import json
from unittest.mock import patch
from stripe.http_client import RequestsClient # to mock the request session
stripe.api_key = "foo"
stripe.default_http_client = RequestsClient() # assigning the default HTTP client
null = None
false = False
true = True
charge_resp = {
"id": "ch_1FgmT3DotIke6IEFVkwh2N6Y",
"object": "charge",
"amount": 1000,
"amount_captured": 1000,
"amount_refunded": 0,
"billing_details": {
"address": {
"city": "Los Angeles",
"country": "USA",
},
"email": null,
"name": "Jerin",
"phone": null
},
"captured": true,
}
def get_customer_city_from_charge(stripe_charge_id):
# this is our function and we are writing unit-test for this function
charge_response = stripe.Charge.retrieve("foo-bar")
return charge_response.billing_details.address.city
class TestStringMethods(unittest.TestCase):
#patch("stripe.default_http_client._session")
def test_get_customer_city_from_charge(self, mock_session):
mock_response = mock_session.request.return_value
mock_response.content.decode.return_value = json.dumps(charge_resp)
mock_response.status_code = 200
city_name = get_customer_city_from_charge("some_id")
self.assertEqual(city_name, "Los Angeles")
if __name__ == '__main__':
unittest.main()
Advantages of this method
You can generate the corresponding class objects (here, the charge_response variable is a type of Charge--(source code))
You can use the dot (.) operator over the response (as we can do with real stripe SDK)
dot operator support for deep attributes

Django Admin: populate the field based on previous field value

I have a model in django admin as follows
ChoiceA= (
("on-false","on-false"),
("on-true","on-true"),
)
ChoiceB = (
("always","always"),
("never","never"),
)
id = models.CharField(verbose_name="Field",max_length=32)
type = models.CharField(verbose_name="Expression",max_length=32)
action = models.CharField(max_length=32, choices=x)
Now based on the type entered by the user ie if user enters type = "a" then action's choices should be set to ChoiceA and if user enters type ="b" then action's choices should be set to ChoiceB. How can I achieve this in Django Admin?
Edit:
action_change.js
jQuery(document).ready(function(){
$("#id_type").change( function(event) {
$.ajax({
"type" : "POST",
"url" : "/action_choices/",
"dataType" : "json",
"cache" : false,
"error" : alert("hello"),
"success" : function(json) {
$('#id_action >option').remove();
for(var j = 0; j < json.length; j++){
$('#id_action').append($('<option></option>').val(json[j][0]).html(json[j][1]));
}
}
});
});
});
You can achieve it using Ajax and jQuery:
models.py:
type = models.CharField(verbose_name="Expression",max_length=32)
action = models.CharField(max_length=32, choices = (('', ''), ))
admin.py:
class MyModelAdmin(admin.ModelAdmin):
list_display = ('type', )
class Media:
js = ['/static/js/action_change.js']
admin.site.register(MyModel, MyModelAdmin)
urls.py:
url(r'^action_choices/', 'myproject.myapp.views.action_choices'),
views.py:
def action_choices(request):
action_list = []
ChoiceA = ("on-false", "on-true")
ChoiceB = ("always", "never")
action_type = request.GET.get('action_type')
if str(action_type).lower() == 'a':
choices = ChoiceA
elif str(action_type).lower() == 'b':
choices = ChoiceB
else:
choices = ()
[action_list.append((each,each)) for each in choices]
json = simplejson.dumps(action_list)
return HttpResponse(json, mimetype='application/javascript')
Create the file action_change.js with following content in your static folder and define correct path in class Media of ModelAdmin.
action_change.js
(function($){
$(function(){
$(document).ready(function() {
$('#id_type').bind('keyup', type_change);
$('#id_action >option').show();
});
});
})(django.jQuery);
// based on the type, action will be loaded
var $ = django.jQuery.noConflict();
function type_change()
{
var action_type = $('#id_type').val();
$.ajax({
"type" : "GET",
"url" : "/action_choices/?action_type="+action_type,
"dataType" : "json",
"cache" : false,
"success" : function(json) {
$('#id_action >option').remove();
for(var j = 0; j < json.length; j++){
$('#id_action').append($('<option></option>').val(json[j][0]).html(json[j][1]));
}
}
})(jQuery);
}
This should work fine for the scenario you asked. And I'm giving my suggestion below:
models.py
type = models.CharField(verbose_name="Expression",max_length=32, choices = (('a', 'a'), ('b', 'b'), ))
action = models.CharField(max_length=32, choices = (('', ''), ))
action_change.js (line 5)
$('#id_type').bind('change', type_change);
You would have to initialize the action field with all possible choices, or Django will complain that a choice that didn't previously exist isn't a valid choice.
My recommendation would be to initialize the field with all of the possible choices, and use JavaScript to toggle the visibility of the choices, depending on the value of type. There are a few plugins around that will handle dynamic fields in Django admin, but most that I've seen deal with ForeignKey or ManyToMany fields that need to do lookups.
You're probably best off just adding some JavaScript to your admin form via the Media meta class and handling it yourself.