How to update an existing record in Django? - django

I am trying to update an existing record in Django but whenever I PUT request with postman I get the following.
It says that it already exists. So i tried to assign
task_1 = Task.objects.get(pk=task_id)
task_1 = ser
But I get an error that i can't assign it
===============================
My current code:
class TaskView(APIView):
permission_classes = (IsAuthenticated,)
def put(self, request, task_id):
task_obj = find_task(request.auth, task_id)
if task_obj:
task = task_obj.data
my_data = json.loads(request.body)
keys = list(my_data.keys())
for key in keys:
task[key] = my_data[key]
ser = TaskSerializer(data=task)
ser.is_valid(raise_exception=True)
ser.save()
return Response(ser.data)
else:
return Response(status=status.HTTP_404_NOT_FOUND)
# Returns the task only if its assigned to current user
def find_task(token, task_id):
u_id = Token.objects.get(key=token).user_id
assign_to = AssignTo.objects.filter(task_id=task_id, user_id=u_id)
if len(assign_to) > 0:
task = Task.objects.get(pk=task_id)
serializer = TaskSerializer(task)
return serializer
else:
return False
I believe it has nothing to do with the method i use ( POST, PUT, PATCH) because it works perfect when I comment out this part of code:
# ser = TaskSerializer(data=task)
# print(ser)
# ser.is_valid(raise_exception=True)
# ser.save()
return Response(task)
So it's something with serialization and task Instances
===============================
SECOND VERSION OF MY CODE:
def put(self, request, task_id):
u_id = Token.objects.get(key=request.auth).user_id
assign_to = AssignTo.objects.filter(task_id=task_id, user_id=u_id)
if len(assign_to) > 0:
task = Task.objects.get(pk=task_id)
my_data = json.loads(request.body)
keys = list(my_data.keys())
for key in keys:
task[key] = my_data[key]
print(task)
return Response(task)
else:
return Response(status=status.HTTP_404_NOT_FOUND)
The error I get from my SECOND VERSION of code is :
TypeError: 'Task' object does not support item assignment
So how do I update JUST the given keys from the request body.

So the solution was pretty simple.
I just had to add some args inside the save function => task.save(update_fields=keys)
This is my working code:
def put(self, request, task_id):
# task = Task.objects.get(pk=task_id)
# Task is the returned Model value
task = find_task(request.auth, task_id)
if task:
my_data = json.loads(request.body)
keys = list(my_data.keys())
for key in keys:
setattr(task, key, my_data[key])
task.save(update_fields=keys)
ser = TaskSerializer(task)
return Response(ser.data)
else:
return Response(status=status.HTTP_404_NOT_FOUND)

Related

Best approach to implement server-side caching on a Django GraphQL API?

I have an Angular frontend that uses Apollo Graphql client to interface with a Django Graphql backend (using graphene). It all works well, but the API is very slow, especially when there are concurrent users. I am trying a few things to speed up the API server. One of the things that I am seriously considering is to use server-side caching.
I'm aware that there are some caching solutions that exist for Django out of the box, but these seem to be for caching the views. Is it possible to use these methods to cache Graphql API requests?
What is the most effective caching solution for caching the responses to graphql queries in Django?
I'm posting some of my project's code here so that you get a sense of how my API works.
This is a sample of the Query class containing the various graphql endpints that serve data when queries are received from the client. And I suspect this is where we'll need most of the optimization and caching applied:-
class Query(ObjectType):
# Public Queries
user_by_username = graphene.Field(PublicUserType, username=graphene.String())
public_users = graphene.Field(
PublicUsers, searchField=graphene.String(), membership_status_not=graphene.List(graphene.String), membership_status_is=graphene.List(graphene.String), roles=graphene.List(graphene.String), limit=graphene.Int(), offset=graphene.Int())
public_institution = graphene.Field(PublicInstitutionType, code=graphene.String())
public_institutions = graphene.Field(PublicInstitutions, searchField=graphene.String(), limit = graphene.Int(), offset = graphene.Int())
public_announcement = graphene.Field(AnnouncementType, id=graphene.ID())
public_announcements = graphene.List(
AnnouncementType, searchField=graphene.String(), limit=graphene.Int(), offset=graphene.Int())
def resolve_public_institution(root, info, code, **kwargs):
institution = Institution.objects.get(code=code, active=True)
if institution is not None:
public_institution = generate_public_institution(institution)
return public_institution
else:
return None
def resolve_public_institutions(root, info, searchField=None, limit=None, offset=None, **kwargs):
qs = Institution.objects.all().filter(public=True, active=True).order_by('-id')
if searchField is not None:
filter = (
Q(searchField__icontains=searchField.lower())
)
qs = qs.filter(filter)
total = len(qs)
if offset is not None:
qs = qs[offset:]
if limit is not None:
qs = qs[:limit]
public_institutions = []
for institution in qs:
public_institution = generate_public_institution(institution)
public_institutions.append(public_institution)
public_institutions.sort(key=lambda x: x.score, reverse=True) # Sorting the results by score before proceeding with pagination
results = PublicInstitutions(records=public_institutions, total=total)
return results
#login_required
def resolve_institution_by_invitecode(root, info, invitecode, **kwargs):
institution_instance = Institution.objects.get(
invitecode=invitecode, active=True)
if institution_instance is not None:
return institution_instance
else:
return None
#login_required
#user_passes_test(lambda user: has_access(user, RESOURCES['INSTITUTION'], ACTIONS['GET']))
def resolve_institution(root, info, id, **kwargs):
current_user = info.context.user
institution_instance = Institution.objects.get(pk=id, active=True)
allow_access = is_record_accessible(current_user, RESOURCES['INSTITUTION'], institution_instance)
if allow_access != True:
institution_instance = None
return institution_instance
#login_required
#user_passes_test(lambda user: has_access(user, RESOURCES['INSTITUTION'], ACTIONS['LIST']))
def resolve_institutions(root, info, searchField=None, limit=None, offset=None, **kwargs):
current_user = info.context.user
qs = rows_accessible(current_user, RESOURCES['INSTITUTION'])
if searchField is not None:
filter = (
Q(searchField__icontains=searchField.lower())
)
qs = qs.filter(filter)
total = len(qs)
if offset is not None:
qs = qs[offset:]
if limit is not None:
qs = qs[:limit]
results = Institutions(records=qs, total=total)
return results
#login_required
def resolve_user(root, info, id, **kwargs):
user_instance = User.objects.get(pk=id, active=True)
if user_instance is not None:
user_instance = redact_user(root, info, user_instance)
return user_instance
else:
return None
def resolve_user_by_username(root, info, username, **kwargs):
user = None
try:
user = User.objects.get(username=username, active=True)
except:
raise GraphQLError('User does not exist!')
courses = Report.objects.filter(active=True, participant_id=user.id)
if user is not None:
user = redact_user(root, info, user)
title = user.title if user.title else user.role.name
new_user = PublicUserType(id=user.id, username=user.username, name=user.name, title=title, bio=user.bio, avatar=user.avatar,institution=user.institution, courses=courses)
return new_user
else:
return None
def process_users(root, info, searchField=None, all_institutions=False, membership_status_not=[], membership_status_is=[], roles=[], unpaginated = False, limit=None, offset=None, **kwargs):
current_user = info.context.user
admin_user = is_admin_user(current_user)
qs = rows_accessible(current_user, RESOURCES['MEMBER'], {'all_institutions': all_institutions})
if searchField is not None:
filter = (
Q(searchField__icontains=searchField.lower()) | Q(username__icontains=searchField.lower()) | Q(email__icontains=searchField.lower())
)
qs = qs.filter(filter)
if membership_status_not:
qs = qs.exclude(membership_status__in=membership_status_not)
if membership_status_is:
qs = qs.filter(membership_status__in=membership_status_is)
if roles:
qs = qs.filter(role__in=roles)
redacted_qs = []
if admin_user:
redacted_qs = qs
else:
# Replacing the user avatar if the requesting user is not of the same institution and is not a super admin
for user in qs:
user = redact_user(root, info, user)
redacted_qs.append(user)
pending = []
uninitialized = []
others = []
for user in redacted_qs:
if user.membership_status == User.StatusChoices.PENDINIG:
pending.append(user)
elif user.membership_status == User.StatusChoices.UNINITIALIZED:
uninitialized.append(user)
else:
others.append(user)
sorted_qs = pending + uninitialized + others
total = len(sorted_qs)
if unpaginated == True:
results = Users(records=sorted_qs, total=total)
return results
if offset is not None:
sorted_qs = sorted_qs[offset:]
if limit is not None:
sorted_qs = sorted_qs[:limit]
results = Users(records=sorted_qs, total=total)
return results
#login_required
def resolve_users(root, info, searchField=None, membership_status_not=[], membership_status_is=[], roles=[], limit=None, offset=None, **kwargs):
all_institutions=False
unpaginated = False
qs = Query.process_users(root, info, searchField, all_institutions, membership_status_not, membership_status_is, roles, unpaginated, limit, offset, **kwargs)
return qs
def resolve_public_users(root, info, searchField=None, membership_status_not=[], membership_status_is=[], roles=[], limit=None, offset=None, **kwargs):
all_institutions=True
unpaginated = True
results = Query.process_users(root, info, searchField, all_institutions, membership_status_not, membership_status_is, roles, unpaginated, limit, offset, **kwargs)
records = results.records
total = results.total
public_users = []
# This is to limit the fields in the User model that we are exposing in this GraphQL query
for user in records:
courses = Report.objects.filter(active=True, participant_id=user.id)
score = 0
for course in courses:
score += course.completed * course.percentage
new_user = PublicUserType(id=user.id, username=user.username, name=user.name, title=user.title, bio=user.bio, avatar=user.avatar,institution=user.institution, score=score)
public_users.append(new_user)
public_users.sort(key=lambda x: x.score, reverse=True) # Sorting the results by score before proceeding with pagination
if offset is not None:
public_users = public_users[offset:]
if limit is not None:
public_users = public_users[:limit]
results = PublicUsers(records=public_users, total=total)
return results
I'm not sure what other code you'll need to see to get a sense, because the rest of the setup is typical of a Django application.

why i am getting this error KeyError at /evaluationtest/? 'request'

hi i have create an Crud function for evaluation Test but i am getting keyerror 'request' this kind of strange to me i have not seen this error before i am new to django can somebody help me to fix it?
def validate(self, data, *args, **kwargs):
questions = self.context['request'].data.get("questions")
if not questions:
raise serializers.ValidationError("questions are required")
if self.context["request"].method == "POST":
self.questions = QuestionSerializer(data=questions, many=True)
self.questions.is_valid(raise_exception=True)
elif self.context["request"].method == "PUT":
self.questions = questions
self.new_questions = self.context["request"].data.get(
"new_questions")
if self.new_questions:
self.new_questions = QuestionSerializer(
data=self.new_questions, many=True)
self.new_questions.is_valid(raise_exception=True)
return data
def create(self, data):
evaluation_test = EvaluationTest()
evaluation_test.category = data['category']
evaluation_test.admin = data['admin']
evaluation_test.title = data['title']
evaluation_test.type = data['type']
evaluation_test.save()
for question in data['questions']:
question.evaluationtest = evaluation_test
question.save()
return evaluation_test
def update(self, instance, validated_data):
instance.title = validated_data.get["title", instance.title]
instance.type = validated_data.get["type", instance.type]
instance.category = validated_data.get["category", instance.category]
instance.admin = validated_data.get["admin", instance.admin]
for question in self.questions:
q = QuestionSerializer(instance=question["id"], data=question)
q.is_valid(raise_exception=True)
q.save()
if self.new_questions:
new_questions = self.new_questions.save()
for question in new_questions:
question.save()
return instance
views.py
Here is my view.py code when i am implementing the whole crud application you can see and please give me how can i modify to make it work
class EvaluationTestViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated, )
serializer_class = EvaluationTestSerializer
queryset = EvaluationTest.objects.all()
#action(methods=['get'], detail=False, url_path='by-category/(?P<category_id>\d+)', url_name='by_category')
def by_category(self, request, *args, **kwargs):
evaluationtest = EvaluationTestSerializer.by_category(
kwargs['category_id'])
if evaluationtest:
return Response(evaluationtest)
return Response(status=HTTP_404_NOT_FOUND)
def create(self, request):
serializer = EvaluationTestSerializer(data=request.data)
if serializer.is_valid():
evaluationtest = serializer.create(request)
print(evaluationtest)
if evaluationtest:
return Response(status=HTTP_201_CREATED)
return Response(status=HTTP_400_BAD_REQUEST)
you have to set the request in the serializer context.
change your create method:
def create(self, request):
serializer = EvaluationTestSerializer(data=request.data, context={'request': request})
if serializer.is_valid():
# call save() instead of create
evaluationtest = serializer.save()
print(evaluationtest)
# ideally you should also return serializer.data
return Response(status=HTTP_201_CREATED)
# ideally you should also return serializer.errors
return Response(status=HTTP_400_BAD_REQUEST)

Save request.POST to database

in view.py:
#require_POST
#csrf_exempt
def ipn(request):
transactions_logger = logging.getLogger("django")
processor = Ipn(request.POST, logger=transactions_logger)
verification_success = processor.verify_ipn()
encoding = request.POST.get('ok_charset', None)
data = QueryDict(request.body, encoding=encoding)
if verification_success:
form = OkpayIpnForm(data)
if form.is_valid():
print("ALL FINE!!")
form.save()
return HttpResponse("")
In forms.py:
class OkpayIpnForm(forms.ModelForm):
class Meta:
model = OkpayIpn
exclude = []
Code for IPN Checkprocessor = Ipn(request.POST, logger=transactions_logger:
class Ipn(object):
OKPAY_VERIFICATION_URL = 'https://checkout.okpay.com/ipn-verify'
OKPAY_IPN_INVALID = b'INVALID'
OKPAY_IPN_VERIFIED = b'VERIFIED'
OKPAY_IPN_TEST = b'TEST'
OKPAY_STATUS_COMPLETED = 'completed'
__verification_result = False
def __init__(self, request_data, logger):
if 'ok_verify' in request_data:
raise Exception("ok_verify must not be present in initial request data for {}".format(
self.__class__.__name__
))
self._request_data = request_data
self.logger = logger
return
def verify_ipn(self):
self.__verification_result = False
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
verify_request_payload = {
'ok_verify': 'true',
}
verify_request_payload.update(self._request_data)
resp = requests.post(self.OKPAY_VERIFICATION_URL, data=verify_request_payload, headers=headers)
if resp.content == self.OKPAY_IPN_VERIFIED or resp.content == self.OKPAY_IPN_TEST:
self.__verification_result = True
# if resp.content == self.OKPAY_IPN_VERIFIED: # anyway disable test on production.
# self.__verification_result = True
return self.__verification_result
All is ok, I revice IPN and validate it, then I try to validate form and save it to Database.
But form doesn't pass validation and doesn't save to database.
Thank You for help
Problem was that 1 CharField of Model for saving IPN has maxlength=20, but recieved 40 symbols.
Thx jape he advised to add in form validation else statement and print form.errors
the error of of form validation was :
<li>ok_item_1_type<ul class="errorlist"><li>Ensure this value has at most 20 characters (it has 40).</li></ul></li>

Flask login 'function' object has no attribute 'is_active'

I try to use flask login in may app:
My controller:
#app.route("/process_log", methods=['POST'])
def process_login():
filled_form = LoginForm(request.form)
if filled_form.validate():
phone = filled_form.phone.data
password = filled_form.password.data
if User.phone_exists(phone) and User.pass_match(phone, password):
user = User.get_by_phone(phone)
login_user(user.get_id)
return redirect(url_for("index"))
else:
return render_template("login.html", form = filled_form, error = u"Не верный логин или пароль")
else:
return render_template("home.html", form = filled_form)
and I have some class with defined functions which required for API of flask login
My User class:
from pymongo import MongoClient
from bson.objectid import ObjectId
class User():
client = MongoClient()
db = client['test']
col = db['user']
user_id = None
def __init__(self, dic):
self.dic = dic
def is_authenticated():
return True
def is_anonymous():
return False
def is_active():
return True
def get_id(self):
return unicode(str(self.user_id))
def save(self):
self.user_id = self.col.insert(self.dic)
print "Debug:" + str(self.user_id)
#staticmethod
def _get_col():
client = MongoClient()
db = client['test']
col = db['user']
return col
#staticmethod
def phone_exists(phone):
col = User._get_col()
if col.find_one({'phone': phone}) != None:
return True
else:
return False
#staticmethod
def pass_match(phone, password):
col = User._get_col()
if col.find_one({'phone': phone})['password'] == password:
return True
else:
return False
#staticmethod
def get(userid):
col = User._get_col()
return col.find_one({'_id':userid})
#staticmethod
def get_by_phone(phone):
col = User._get_col()
dic = col.find_one({'phone': phone})
print dic['password']
return User(dic)
As you see function is_active is defined(Note:I also tried to pass refference with self)
But I still have this error AttributeError: 'function' object has no attribute 'is_active'
I am sorry for too much code here, but it should be pretty straightforward.
Note: I am using mongodb for my project.
Please help me to find my error. Thank you too much
One more thing:
Should I provide login_user(....) with Id or with my user object?
You must sent to login_user User instance (not id), see: https://github.com/maxcountryman/flask-login/blob/master/flask_login.py#L576.
So next code must work:
user = User.get_by_phone(phone)
login_user(user)

Access missing value in form.cleaned_data

I was trying to dynamically generate fields as shown in http://jacobian.org/writing/dynamic-form-generation/. My case slightly differs in that I am looking to use multiplechoicefield that is dynamically created. This is what I came up with...
views.py
def browseget(request):
success = False
if request.method == 'POST':
list_form = ListForm(request.POST)
if list_form.is_valid():
success = True
path = list_form.cleaned_data['path']
minimum_size = list_form.cleaned_data['minimum_size']
follow_link = list_form.cleaned_data['follow_link']
checkboxes = list_form.cleaned_data['checkboxes']
....do something
else:
list_form = ListForm(name_list)
ctx = {'success': success, 'list_form': list_form, 'path': path, 'minimum_size': minimum_size}
return render_to_response('photoget/browseget.html', ctx, context_instance=RequestContext(request))
forms.py
class ListForm(forms.Form):
path = forms.CharField(required=False)
minimum_size = forms.ChoiceField(choices=size_choices)
follow_link = forms.BooleanField(required=False, initial=True)
def __init__(self, *args, **kwargs):
name_list = kwargs.pop('name_list', None)
super(ListForm, self).__init__(*args, **kwargs)
print 'Received data:', self.data
if name_list:
name_choices = [(u, u) for u in name_list]
self.fields['checkboxes'] = forms.MultipleChoiceField(required=False, label='Select Name(s):', widget=forms.CheckboxSelectMultiple(), choices=name_choices)
def clean_path(self):
cd = self.cleaned_data
path = cd.get('path')
if path == '': path = None
return path
def clean_minimum_size(self):
cd = self.cleaned_data
minimum_size = cd.get('minimum_size')
if minimum_size is None: minimum_size = 0
return int(minimum_size)
The form generates and displays perfectly... until I post some data. The 'checkboxes' field doesn't show up in list_form.cleaned_data.items() while it shows in self.data. As it is the form breaks with a KeyError exception. So Im asking, how do i access the checkboxes data?
You're not passing in the name_list parameter when you re-instantiate the form on POST, so the field is not created because if name_list is False.