What's the django way to handle votes on objects? - django

class Survey(models.Model):
answers = models.ManyToMany('Answer')
class Answer(models.Model):
answer = models.CharField(max_length=30)
votes = models.IntegerField(default=0)
I want to display in html a list of answers and their vote count, and have for each of them a simple "+1" button that would increment the votes value.
If I had to reinvent the wheel, I would call a view upvote(answer_id) that would fetch the answer in the database, increment the votes and save, and also would do the same in javascript to update the corresponding field.
Is there a better way to do this in Django ?
Same question if in the html I allow the user to post a new answer.

All you have to do is a AJAX calls with a JSON response. Then update your DOM with javascript.
This is a dummy example:
Django side:
import json
def upvote(request, anwser_id):
# You shall write a better test
a = Answser.objects.get(pk=anwser_id)
a.votes += 1
a.save()
return HttpResponse(json.dumps({
'count': a.votes,
'pk': anwser_id
}, content_type="application/json")
Client site (assuming jQuery) - no need to post :
$.getJSON('/upvote/' + your_anwser_id + '/')
.done(function(data) {
// You've got your response here
$("YOUR-INPUT-SELECTOR").html(data.count);
})
.fail(function(xhr) {
// Server error
alert("Error: "+ xhr.responseText);
});
If someone votes between two queries, you'll have the correct anwser. I use that kind of solution for a like/dislike comments voting system.

Related

Query set too many objects to unpack expected(2) in django templateView

I have written a view to show open, completed, accepted and closed tickets on dashboard on clicking which goes into particular url to display the tickets accordingly and am switching the templates according to their status, I am querying the ticket status and I get the following error
Too many objects to unpack (expected 2)
models.py
class Modes(models.Model):
Error is due to this query...
Ticket.objects.filter('status')
You should specify something like:
if Ticket.objects.filter(status='Opened'):
template_name = 'app/open_tickets.html')
Also please check the logic. Above query makes no sense in get_template_name... You should be getting one template according to request. So if request is for 'opened' then you should get template only for 'opened'.
Suppose if you want to get for 'opened' you need to pass it in query and do this:
if self.request.GET.get('status', '') == 'Opened':
template_name = 'app/open_tickets.html')
def show(request,status):
if status.title() in ['O','A,'C','Cl']:
if status.title() == 'O':
models = Model.objects.filter(status='O')
else:
models = Model.objects.filter(status=status.title(),accepted_by=request.user)
templates = {'O':'p/op.html','A':'p/d.html','C':'p/pp.html','Cl:'a.html'}
return render(request,templates[status.title()],{'tickets':tickets})
inside your urls.py
path('models/<str:status>/',views.show,name='show')

How to perform the delete method in drf django

How to perform the delete request in Django drf? How will I pass the params for the request?
Kindly help with the solution. I am very new in this drf-django-python programming.
class DeleteView(APIView):
def delete(self, request,format=None):
id = request.POST['book_id']
email = request.POST['email']
book = models.Book.objects.filter(book_id=id)
book_uploader = serializers.BookSerializer(book[0]).data['uploader']['email']
logged_in = request.user
print(log)
if book_uploader == logged_in :
books = models.BookUserRelationship.objects.filter(book= id, user__email=email)
books.delete()
return Response("Successfully removed", status=status.HTTP_204_NO_CONTENT)
else :
return Response("Not able to remove")
In comments you noticed that parameters will be embedded in url but you are trying to get values from POST dict.
If your url is something like
/books/id/email/
You should use request.kwargs dict like request.kwargs.get('email')
But if your url is like
/books/id/?email=someemail#google.com
id would be in request.kwargs but email in request.query_params
Notice that every url variable is in the request.query_params dict.
IMPORTANT
If you have ID url param without named group, viewset would not be able to get this from request.kwargs by name

Is APITest with Query params different then just normal url?

I'm writing some unit tests against an API that either returns all the books, or only returns the books of the given genre in the query params. This seems to be working when I hit it in my local dev server. However, it doesn't even go into the else statement if the genre is specified in my unit test.
My unit test looks like this:
class TitlesAndBlurbsListTestCase(APITestCase):
def setUp(self):
# Creates a lot of books with genre horror
# and books not in the horror genre
def test_horror_genre(self):
# Ensure that screener can see all the available books
self.client.login(username='b', password='b')
response = self.client.get('/api/titles-and-blurbs/?genre=horror')
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Ensure that the screener gets all horror books at first
horror_books = TitlesAndBlurbs.objects.filter(genre='horror')
# I keep getting an assertion error here - it returns all the books
self.assertEqual(len(response.data), horror_books.count())
My api viewset looks like this
class TitlesAndBlurbsListViewSet(viewsets.mixins.ListModelMixin,
viewsets.mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
model = TitlesAndBlurbs
permission_classes = [ScreenerPermissions]
serializer_class = TitlesAndBlurbsSerializer
def get_queryset(self):
if self.action == 'list':
genre = self.request.QUERY_PARAMS.get('genre', None)
if not genre:
print 'i dont have query params of genre'
TitlesAndBlurbs.objects.all()
else:
print genre
TitlesAndBlurbs.objects.filter(genre=genre)
return TitlesAndBlurbs.objects.all()
my url/router looks like
router.register(r'api/titles-and-blurbs', TitlesAndBlurbsListViewSet)
When I hit the url 'api/titles-and-blurbs/?genre=horror' in my browser I get the print statement and titles and blurbs that have the genre horror. However, when I hit in the
test suite, I don't get the print statement genre I get the print statement of 'i dont have query params', and it returns all books. Any help is really
appreciated.
Try passing the query parameter as a data payload instead. Change the line in your test to:
response = self.client.get('/api/titles-and-blurbs/', {'genre': 'horror'})
Django docs here on the different ways to pass query parameters in urls.
Another person reported a similar issue with an empty QUERY_PARAMS while testing DRF (see here). It looks like they fixed it but maybe they missed something or you didn't get the update.
If someone comes across this, for me it helped to:to change the url from
/api/titles-and-blurbs?genre=horror
to
/api/titles-and-blurbs/?genre=horror
Both urls were working find in Postman, but only the second one is working properly in the tests.

Using FullCalendar with Django Views

I'm trying to implement the FullCalendar on my website, but am a little new to this and not quite sure how to format what I want to do. I have a view which will grab all of an individual user's events. I want to take those events and populate the calendar with them. My issue is that I don't know what to return in the view or how to handle that return value in the JavaScript function. Here's what I have right now:
View:
def calEvents(request):
user = request.user.get_profile()
eventList = user.eventList
ownedList = user.ownedEvent
events = #Part I'm having trouble with
return HttpResponse(events)
The eventList and ownedEvent keep track of all of a user's events. They have names/dates associated with them. What I don't understand is the format I need to put all that information in to return in my HttpResponse.
My JavaScript function is:
$(document).ready(function() {
$('#calendar').fullCalendar({
eventSources: [
{
url: '/calEvents/',
editable: false,
}
]
});
});
I'm telling it to go to the Django view, but am lost after that. Thanks so much in advance!
I have done this by building a list of dictionaries in my Django view, setting at minimum the required fields of 'title' and the start time, then using simplejson.dumps with cls=DjangoJSONEncoder to return json.
from django.core.serializers.json import DjangoJSONEncoder
def calEvents(request):
# as above, then:
events = []
for event in eventList:
events.append({'title': event.name, 'start': event.start})
# something similar for owned events, maybe with a different className if you like
return HttpResponse(simplejson.dumps(events, cls=DjangoJSONEncoder), mimetype='application/json')
You may also with to limit the events you return based on the starting and ending times provided by the get request:
def calEvents(request):
user = request.user.get_profile()
start_timestamp = request.GET.get('start')
end_timestamp = request.GET.get('end')
start_datetime = datetime.datetime.fromtimestamp(float(start_timestamp))
end_datetime = datetime.datetime.fromtimestamp(float(end_timestamp))
eventList = user.eventList.filter(start_time__lte=end_datetime, end_time__gte=start_datetime)
I am neglecting error handling for the timestamp conversion - fullcalendar will give you appropriate values, but it would be best to allow for the possibility of bad input. And I am making assumptions about the structure of your event models.

Django- Many to Many field querying

I have following structure of models in django :
class BodySubPart(models.Model):
body_subpart=models.CharField(max_length=20)
def __str__(self):
return self.body_subpart
class BodyPart(models.Model):
body_part=models.CharField(max_length=20)
body_subpart=models.ManyToManyField(BodySubPart)
def __str__(self):
return self.body_part
Ex:
example,
if BodyPart=head then BodySubPart= face,mouth,eyes,nose.
if BodyPart=arm then BodySubPart= shoulder,fingers,elbow.
like this many body parts are stored.
...
now I want to create a runtime form have two choicefields (BodySubPart and BodyPart) such that when i select the BodyPart it should change the list in BodySubPart.
Ex.
The first choicefield has body parts={head,arm,chest...}
The second choice field should change when i select a particular part
If i select "head" then second choice field should show,
body sub parts={face,mouth,eyes,nose...}
Please help me here.....
What have you tried?? I think you will find people are more willing to help you if you have actually tried something yourself and not just want others to do it for you. It should go something like this:
1) BodyPart.objects.all() # all body parts
2) head = BodyPart.objects.get(body_part='head')
head_subparts = head.body_subpart.all() # all head subparts
django does a great job of explaining how to query these relationships.
https://docs.djangoproject.com/en/dev/topics/db/models/#many-to-many-relationships
In addition there are a number of really great tutorials online regarding djangos' manytomany relationships.
This requires a bit of AJAX, so first step is to create a view to handle that:
from django.core import serializers
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_list_or_404
def ajax_get_bodysubparts(request):
bodypart_id = request.GET.get('bodypart_id')
if bodypart_id:
bodysubparts = get_list_or_404(BodySubPart, bodypart__id=bodypart_id)
data = serializers.serialize('json', bodysubparts)
return HttpResponse(data, mimetype='application/json')
else:
return HttpResponseBadRequest()
Tie that to some URL in urls.py. Then, some JavaScript for your form (assumes jQuery):
$(document).ready(function(){
$('#id_bodypart').change(function(){
var selected = $(this).val();
if (selected) {
$.getJSON('/url/to/ajax/view/', {
'bodypart_id': selected
}, function (data, jqXHR) {
options = [];
for (var i=0; i<data.length; i++) {
options.append('<option value="'+data[i].id+'">'+data[i].body_subpart+'</option>');
}
$('#id_bodysubpart).html(options.join(''));
});
}
});
});
You will probably need a combination of custom form fields and widgets to get what you want.
Check out the django-ajax-filtered-fields project to see if that is close what you are looking for. It will at least provide some guidance if you decide to create your own.
You will need some javascript to make a new request to populate your fields dynamically, so that will also not be available with standard django forms.