Django: Use a QuerySet in Function - django

I want to make some tournament matches in a DetailView. But I can't figure out how to make a query of all registrations context['regs'] and use the queryset in my function create_matches().
class KategorieDetail(DetailView):
model = Kategorie
context_object_name = 'kategorie'
def create_matches(regs):
red_corner = []
blue_corner = []
matches = []
# Separate the regs into both corners
i = 1
for reg in regs:
if i%2 == 1:
red_corner.append(reg)
else:
blue_corner.append(reg)
i += 1
# Create Match-Ups
while blue_corner:
match = {'red': red_corner.pop(), 'blue': blue_corner.pop()}
matches.append(match)
return matches
def get_context_data(self, **kwargs):
context = super(KategorieDetail, self).get_context_data(**kwargs)
kid = context['kategorie'].id
context['regs'] = Registrierung.objects.filter(kategorie=context['kategorie'].id)
context['regs_count'] = context['regs'].count()
context['matches'] = create_matches(context['regs'].values(), kid)
return context
In my HTML-View I can't display the matches. If I say {{matches}}, I get:
HttpResponseRedirect status_code=302, "text/html; charset=utf-8", url="/events/"
I also don't get why I have to give the Kategorie_ID to the create_matches(regs) function.

Related

How to render form with errors in generic detail view

I am trying to render a generic detail view page with a form with errors
My post method in my generic detail view is
def post(self, request, slug):
if 'submit-edit-roster' in request.POST:
edit_roster_form = EditRosterForm(request.POST, team=self.request.user.playerprofile.team)
if edit_roster_form.is_valid():
edit_roster_form.save()
return redirect ('tcl-team', slug=self.request.user.playerprofile.team.urlslug)
my edit roster form is
class EditRosterForm(forms.Form):
members = 0
team = None
sublist = []
playerlist = []
def __init__(self, *args, **kwargs):
self.team = kwargs.pop('team', None)
self.members = 0
self.sublist = []
self.playerlist = []
super(EditRosterForm, self).__init__(*args, **kwargs)
currentroster = Roster.objects.filter(team=self.team)[0]
for member in Playerprofile.objects.filter(team=self.team).order_by('name'):
if member in currentroster.players.all():
self.fields[str(member.name)] = forms.ChoiceField(choices=ROSTER_CHOICES)
self.initial[str(member.name)] = '1'
elif member in currentroster.subs.all():
self.fields[str(member.name)] = forms.ChoiceField(choices=ROSTER_CHOICES)
self.initial[str(member.name)] = '2'
self.members += 1
def clean(self):
cleaned_data = super().clean()
i = 0
for member in Playerprofile.objects.filter(team=self.team):
if cleaned_data[member.name] == '1':
self.playerlist.append(member)
elif cleaned_data[member.name] == '2':
self.sublist.append(member)
i += 1
print(len(self.sublist))
if len(self.sublist) > 2:
raise ValidationError("Maximum of 2 subs allowed")
if len(self.playerlist) > 5:
raise ValidationError("Maximum of 5 players allowed")
if len(self.playerlist) + len(self.sublist) > i:
raise ValidationError("Team members must be a sub or a player")
return cleaned_data
def save(self):
cleaned_data = self.cleaned_data
print(cleaned_data)
UpdateRoster(roster=self.team.GetCurrentRoster(), players=self.playerlist, subs=self.sublist)
When my form has errors I get
The view team.views.TeamEditView didn't return an HttpResponse object. It returned None instead.
I know I need to add something to pass my form the form object with errors but can't find how to do this.
if edit_roster_form.is_valid():
edit_roster_form.save()
return redirect ('tcl-team', slug=self.request.user.playerprofile.team.urlslug)
else:
# Render detail view page with form
Apologies if my question is incoherent, I'm new to django and stackoverflow
I think you have to add request as parameter to the render shortcut

Passing extra variable with Django ListView

Can't pass extra variable with listview
I tried adding another function and returning the value but it then doesn't return the main part.
class PostListView(ListView):
model = Post
template_name = 'blog/home.html' # <app>/<model>_<viewtype>.html
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 3
def get_context_data(self, **kwargs):
posting = []
for post in Post.objects.all():
post_words = post.content.split()
for word in post_words:
posting.append(word.lower())
mostused_word = []
for word in posting:
if len(mostused_word) == 0:
mostused_word.append({'daword': word, 'word_count': posting.count(word)})
else:
if posting.count(word) > mostused_word[0]['word_count']:
mostused_word[0] = {'daword': word, 'word_count': posting.count(word)}
context = {
'mostused_word': mostused_word[0]['daword'],
'mostused_word_count': mostused_word[0]['word_count'],
'postin': posting,
}
return context
I expect to pass both needed variables, not only one of them.
You need to call the super method.
def get_context_data(self, **kwargs):
...
context = {
'mostused_word': mostused_word[0]['daword'],
'mostused_word_count': mostused_word[0]['word_count'],
'postin': posting,
}
kwargs.update(context)
return super().get_context_data(**kwargs)

Override page_size & ordering of CursorPagination in Django Rest Framework

I using CursorPagination of Django Rest Framework, I want to override default page_size(10) and ordering('timestamp') in a single viewset. How can I do this?
I tried with my viewset but it's not success:
from rest_framework.pagination import CursorPagination
class ListAPIView(ListAPIView):
queryset = Cake.objects.all()
permission_classes = [AllowAny]
serializer_class = ListSerializer
pagination_class = CursorPagination
filter_backends = (OrderingFilter, DjangoFilterBackend)
filter_class = CakeListFilter
filterset_fields = ('cake_type', 'user__username')
ordering = '-date'
page_size = 5
You may create a new class inheriting from CursorPagination class in order to set custom page_size and/or max_page_size like so:
class CustomPageSizeCursorPagination(CursorPagination):
page_size = 5
max_page_size = 100
And then use this class as the pagination_class field of your viewset
WARNING: The following code is untested
Another option is to write a custom paginator class that gets the page size from the viewset. For example:
class PageSizeInViewSetCursorPagination(CursorPagination):
def get_page_size(self, request, viewset_page_size):
if self.page_size_query_param:
try:
return _positive_int(
request.query_params[self.page_size_query_param],
strict=True,
cutoff=self.max_page_size
)
except (KeyError, ValueError):
pass
return viewset_page_size or self.page_size
def paginate_queryset(self, queryset, request, view=None):
# Get the page_size from the viewset and then decide which page_size to use
viewset_page_size = getattr(view, 'page_size', None)
page_size = self.get_page_size(request, viewset_page_size)
# What follows is copy/paste of the code from CursorPagination paginate_queryset method
if not self.page_size:
return None
self.base_url = request.build_absolute_uri()
self.ordering = self.get_ordering(request, queryset, view)
self.cursor = self.decode_cursor(request)
if self.cursor is None:
(offset, reverse, current_position) = (0, False, None)
else:
(offset, reverse, current_position) = self.cursor
# Cursor pagination always enforces an ordering.
if reverse:
queryset = queryset.order_by(*_reverse_ordering(self.ordering))
else:
queryset = queryset.order_by(*self.ordering)
# If we have a cursor with a fixed position then filter by that.
if current_position is not None:
order = self.ordering[0]
is_reversed = order.startswith('-')
order_attr = order.lstrip('-')
# Test for: (cursor reversed) XOR (queryset reversed)
if self.cursor.reverse != is_reversed:
kwargs = {order_attr + '__lt': current_position}
else:
kwargs = {order_attr + '__gt': current_position}
queryset = queryset.filter(**kwargs)
# If we have an offset cursor then offset the entire page by that amount.
# We also always fetch an extra item in order to determine if there is a
# page following on from this one.
results = list(queryset[offset:offset + self.page_size + 1])
self.page = list(results[:self.page_size])
# Determine the position of the final item following the page.
if len(results) > len(self.page):
has_following_position = True
following_position = self._get_position_from_instance(results[-1], self.ordering)
else:
has_following_position = False
following_position = None
# If we have a reverse queryset, then the query ordering was in reverse
# so we need to reverse the items again before returning them to the user.
if reverse:
self.page = list(reversed(self.page))
if reverse:
# Determine next and previous positions for reverse cursors.
self.has_next = (current_position is not None) or (offset > 0)
self.has_previous = has_following_position
if self.has_next:
self.next_position = current_position
if self.has_previous:
self.previous_position = following_position
else:
# Determine next and previous positions for forward cursors.
self.has_next = has_following_position
self.has_previous = (current_position is not None) or (offset > 0)
if self.has_next:
self.next_position = following_position
if self.has_previous:
self.previous_position = current_position
# Display page controls in the browsable API if there is more
# than one page.
if (self.has_previous or self.has_next) and self.template is not None:
self.display_page_controls = True
return self.page
Note that in the above example the page_size from the request always takes precedence over whatever you have set up in your code. Then the viewset_page_size is the second in line and lastly the deafult page_size from the Pagination class.
Here is a custom pagination class that extends CursorPagination. It checks for ordering and page_size attributes defined in the viewset and if they exist use them. If not, fallback to original settings defined in the pagination class itself.
class NewsCursorPaginator(CursorPagination):
ordering = 'title'
page_size = 5
# get_page_size do not have view attribute, so we have our custom one
def get_custom_page_size(self, request, view):
viewset_page_size = getattr(view, 'page_size', None)
if viewset_page_size:
self.page_size = viewset_page_size
return super(NewsCursorPaginator, self).get_page_size(request)
def get_ordering(self, request, queryset, view):
viewset_ordering = getattr(view, 'ordering', None)
if viewset_ordering:
self.ordering = viewset_ordering
return super(NewsCursorPaginator, self).get_ordering(request, queryset, view)
def paginate_queryset(self, queryset, request, view=None):
self.page_size = self.get_custom_page_size(request, view)
return super(NewsCursorPaginator, self).paginate_queryset(queryset, request, view)
This implementation takes "limit" (page_size) as an optional querystring parameter.
class CursorPagination(pagination.CursorPagination):
page_size = settings.REST_FRAMEWORK["PAGE_SIZE"]
def get_custom_page_size(self, request, view):
try:
self.page_size = int(request.GET.get("limit"))
except (ValueError, TypeError):
pass
return super().get_page_size(request)
def paginate_queryset(self, queryset, request, view=None):
self.page_size = self.get_custom_page_size(request, view)
return super().paginate_queryset(queryset, request, view)

Django Rest Framework Paging

I am attempting to make my API get return a maximum of 10 per page. This helps me with infinite loading. The API url will be I am trying looks like this:
www.mysite.com/api/test/?user=5&page=1
However, this does not work.
I've followed the official docs here without success.
I have only modified two files, settings.py & rest_views.py.
settings.py-
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'
}
rest_views.py-
from django.core.paginator import Paginator
...
wardrobematch = {
'user': lambda x: ('user__pk', x)
}
class WardrobeListView(APIView):
renderer_classes = (JSONRenderer, )
paginate_by = 10
paginate_by_param = 'page_size'
max_paginate_by = 100
def get(self, request, *args, **kwargs):
filters = {}
for key, value in request.GET.items():
key = key.lower()
if key in wardrobematch:
lookup, val = wardrobematch[key](value.lower())
filters[lookup] = val
qset = (
Analytic.objects
.filter(like=True,**filters)
.order_by('-updated',)
# .distinct('product_id',)
.values('product_id', 'product__name', 'product__brand', 'product__store__store_name', 'product__variation__image__image', 'product__variation__price__price',)
)
return Response(qset)
When using regular ApiView, you should call the pagination API yourself, it will not perform pagination automatically.
I have created a pagination and a serializer mixim. I'm not sure it is best method, but it worked for me.
class SerializerMixim(object):
def serialize_object(self,obj):
"""Serilize only needed fields"""
return NotImplementedError
class PaginationMixim(object):
_serializer = None
def paginate(self,queryset,num=10):
page = self.request.GET.get('page')
paginator = Paginator(queryset, num)
try:
queryset = paginator.page(page)
except PageNotAnInteger:
queryset = paginator.page(1)
except EmptyPage:
queryset = paginator.page(paginator.num_pages)
count = paginator.count
previous = None if not queryset.has_previous() else queryset.previous_page_number()
next = None if not queryset.has_next() else queryset.next_page_number()
if self._serializer:
objects = self._serializer(queryset.object_list,many=True).data
else:
objects = [self.serialize_object(i) for i in queryset.object_list]
data = {'count':count,'previous':previous,
'next':next,'object_list':objects}
return Response(data)
def serialize_object(self,obj):
return {'id':obj.pk}
class WardrobeListView(APIView,PaginationMixim,SerializerMixim):
renderer_classes = (JSONRenderer, )
#_serializer = AnalyticSerializer
def get(self, request, *args, **kwargs):
filters = {}
for key, value in request.GET.items():
key = key.lower()
if key in wardrobematch:
lookup, val = wardrobematch[key](value.lower())
filters[lookup] = val
qset = (
Analytic.objects
.filter(like=True,**filters)
.order_by('-updated',)
# .distinct('product_id',)
return self.paginate(qset)
def serialize_object(self,obj):
return obj.serilized
then you need to create a propery for Analytic model like,
class Analytic(models.Model):
.....
#property
def serilized(self):
summary = {
'id':self.product.id,
'image':self.product.name,
.......
}
return summary
this will also work with django rest serializers
I got your first example working- to me it was clearer and more basic. All I did was add ".object_list" to stop the "is not JSON serializable" error.
This is your answer with my tiny tweak:
class WardrobeListView(APIView):
renderer_classes = (JSONRenderer, )
def get(self, request, *args, **kwargs):
filters = {}
for key, value in request.GET.items():
key = key.lower()
if key in wardrobematch:
lookup, val = wardrobematch[key](value.lower())
filters[lookup] = val
qset = (
Analytic.objects
.filter(like=True,**filters)
.order_by('-updated',)
# .distinct('product_id',)
.values('product_id', 'product__name', 'product__brand', 'product__store__store_name', 'product__variation__image__image', 'product__variation__price__price',)
)
paginator = Paginator(qset, 2) # Show 25 items per page
page = request.GET.get('page')
try:
qset = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
qset = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
qset = paginator.page(paginator.num_pages)
return Response(qset.object_list)

Initial=True not working in django dynamic form

I want pre-checked checkbox, i passed "initial=True" but it is not working. Below is the code.
class permForm(forms.Form):
id = forms.CharField(widget=forms.TextInput(attrs={'id':'user_id'}),required=False)
def __init__(self, data, **kwargs):
super(permForm, self).__init__(data, **kwargs)
# offset_arr=data.split('/')
# menu_id=offset_arr[1]
# user_id=offset_arr[2]
# flag= offset_arr[3]
# if user.has_perm(permission.codename, task):
usr = User.objects.get(id=1)
task = Site.objects.get_current()
for item in list(AdminMenu.objects.filter(parent_id=0)):
permission=Permission.objects.get(id=item.permission_id);
if usr.has_perm(permission.codename, task):
checked=False
else:
checked=True
self.fields['menu_%d' % item.id] = forms.BooleanField(initial=True,label=item.title,required=False)
for childitem in list(AdminMenu.objects.filter(parent_id=item.id)):
cpermission=Permission.objects.get(id=childitem.permission_id);
if usr.has_perm(cpermission.codename, task):
childchecked=True
else:
childchecked=False
self.fields['menu_%s' %childitem.id] = forms.BooleanField(initial=True,label=childitem.title+'%s'%(data),required=False)
However it is working fine for static forms . see the code below
class MyForm(forms.Form):
option = forms.BooleanField(required=False, initial=True)
Is there any trick to pass initial value for dynamically loaded form element ?
Try this:
Instead of
self.fields['menu_%d' % item.id] = forms.BooleanField(initial=True,label=item.title,required=False)
Remove the initial=True parameter when constructing the field and populate its initial
value in the next step like:
self.fields['menu_%d' % item.id] = forms.BooleanField(label=item.title,required=False)
self.fields['menu_%d' % item.id].initial = True
I tried below and it worked:
self.fields['menu_%d' % item.id] = forms.BooleanField(label=item.title, required=False)
self.fields['menu_%d' % item.id].widget.attrs['checked'] = 'checked'