Convert function base view to class base view in Django - django-views

how to convert these View to Class Base View, this view for add order new order and I wanna
change this View to Class Base but I cant
def add_user_order(request):
new_order_form = UserNewOrderForm(request.POST or None)
if new_order_form.is_valid():
order = Order.objects.filter(owner_id=request.user.id, is_paid=False).first()
if order is None:
order = Order.objects.create(owner_id=request.user.id, is_paid=False)
product_id = new_order_form.cleaned_data.get('product_id')
count = new_order_form.cleaned_data.get('count')
if count < 0:
count = 1
product = Product.objects.get_by_id(product_id=product_id)
order.orderdetail_set.create(product_id=product.id, price=product.price, count=count)
# todo: redirect user to user panel
return redirect(f'products/{product_id}/{product.title.replace(" ", "-")}')
return redirect('/')

You can make use of a FormView [Django-doc] where the form_valid function will do most of the logic:
class UserAddOrderView(FormView):
form_class = UserNewOrderForm
def form_valid(self, form):
order, __ = Order.objects.get_or_create(owner_id=request.user.id, is_paid=False)
self.product_id = product_id = new_order_form.cleaned_data.get('product_id')
count = new_order_form.cleaned_data.get('count')
if count < 0:
count = 1
order.orderdetail_set.create(product_id=product_id, price=product.price, count=count)
return super().form_valid(form)
def get_success_url(self):
return f'products/{product_id}/{product.title.replace(" ", "-")}'
It is however not a good idea to construct URLs manually. You can work with the reverse(…) function [Django-doc]. The same with slugs, do not construct slugs yourself, but use slugify(…) [Django-doc]. You should normally also save the slug to the model to which it belongs to make (fast) lookups possible.

Related

How to call the methods of the model in django template?

I have the model of Invoicetracker in Models.py and I want to call the methods of the model in the template.
Model.py
class Invoicetracker(models.Model):
TotalBillAmount = 0
invoicenumber = models.IntegerField()
invoicedate = models.DateTimeField()
BillAmount = models.IntegerField()
TotalPaid = models.IntegerField()
Remark = models.CharField(max_length=100)
def __str__(self):
return self.invoicenumber
def totalbill(self):
total = Invoicetracker.objects.all().aggregate( TotalBillAmount = Sum('BillAmount'))
return total
def totalpaid(self):
total = Invoicetracker.objects.all().aggregate(TotalPaid = Sum('TotalPaid'))
return total
views.py
def invoicemgt(request):
invoiceitem = Invoicetracker.objects.all()
return render(request, 'order.html',{'invoiceitem' : invoiceitem})
I want to call the method totalbill and totalpaid in the order.html template. Can I call this through instance method? or Shall we use an classmethod?
You can use #property decorator above your function like that:
#property
def totalbill(self):
total = Invoicetracker.objects.all().aggregate( TotalBillAmount = Sum('BillAmount'))
return total
#property
def totalpaid(self):
total = Invoicetracker.objects.all().aggregate(TotalPaid = Sum('TotalPaid'))
return total
After that, you can call those function inside your template like:
{{item.totalbill}}
{{item.totalpaid}}
You can simply call these in a variable. So if you passed an Invoicetracker object to a template with the name invoicetracker, you can render this with:
{{ invoicetracker.totalbill }}
Note that you can not use brackets here. If the item is a callable, the template will automatically call it without parameters. Methods that thus have parameters, can not be called, or at least not without some extra "tricks".
That being said, here your method does not depend on the self. So that makes it more fit for a #staticmethod or #classmethod. For example:
class Invoicetracker(models.Model):
# …
#classmethod
def totalbill(cls):
return cls.objects.aggregate(TotalBillAmount=Sum('BillAmount'))['TotalPaid']
#classmethod
def totalpaid(cls):
return cls.objects.aggregate(TotalPaid=Sum('TotalPaid'))['TotalPaid']
Then you can call these methods in your view, and pass the result to the template:
def some_view(request):
total_paid = Invoicetracker.totalbill()
total_bill = Invoicetracker.totalbill()
return render(request, 'order.html', {'total_paid': total_paid, 'total_bill': total_bill})

Render django.forms.fields.ChoiceField object

In my Django Project I have the following Problem:
I would like to have a dynamic Django form. In the first step the user is asked something by the first form. When I get the postmethod the variables should be used for genereating a new form
my views.py
def calc(request):
if request.method =="POST":
get_form = CalculationForm(request.POST)
if get_form.is_valid():
op = get_form.cleaned_data['op']
ab = get_form.cleaned_data['ab']
alternative = AlternativForm(optype = op, wsgroup = ab)
return render(request, 'calculated_lensar.html', {"alternativ" : alternativ})
else:
form = CalculationForm()
return render(request, 'calc.html', {'form': form})
The secondform (postmethod) looks like
class AlternativForm(forms.Form):
praep_button = ((3, 'hallo'), (4, 'tschüss'))
def __init__(self, optype, wsgroup, *args, **kwargs):
super(AlternativForm, self).__init__(*args, **kwargs) #dont know for what this is standing
self.optype = optype
self.wsgroup = wsgroup
self.values = self.read_db()
self.praep_button = self.buttons()
self.felder = self.blub()
self.neu2 = self.myfield_choices()
def read_db(self):
import sqlite3
....
return result #tuple with 15x5 elements
def buttons(self):
praep_button = []
for i in self.values:
praep_button.append((i[4], i[1]))
return praep_button #Just formating result from read_db in tuple(15x2)
def blub(self):
return forms.ChoiceField(widget=forms.RadioSelect, choices=self.praep_button)
myfield = forms.ChoiceField(widget=forms.RadioSelect, choices=praep_button) #print --><django.forms.fields.ChoiceField object at 0x751f9b90>
def myfield_choices(self):
field = self['myfield']
"""i think here is the problem.
Above 'myfield' is a django.forms.fields.ChoiceField object, but here it is rendered to html (like it should be). I have the code from https://stackoverflow.com/questions/6766994/in-a-django-form-how-do-i-render-a-radio-button-so-that-the-choices-are-separat.
But instead i should use field = self.felder (radioselect woth tuple of the db)"""
widget = field.field.widget
attrs = {}
auto_id = field.auto_id
if auto_id and 'id' not in widget.attrs:
attrs['id'] = auto_id
name = field.html_name
return widget.render(name, field.value(), attrs=attrs)
#return widget.get_renderer(name, field.value(), attrs=attrs)
So all in all I hope the problem is clear.
If i am using AlternativForm() i get the constant form. Instead i would like to get a dynamic form. If I access in views.py:
alternative = AlternativForm(optype = op, wsgroup = ab)
alternative = alternativ.felder
than I get . Can I render that to html?
If I set in forms.py:
field = self.felder
than I get the error that it is a field and not a widget
Thank you for reading!
You just need to assign the choices in the form's __init__() method. Almost what you're doing, but instead of defining self.felder to be a field, you need to use the already initialised form's fields:
myfield = forms.ChoiceField(widget=forms.RadioSelect, choices=praep_button)
def __init__(self, optype, wsgroup, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['myfield'].choices = self.get_choices(optype, wsgroup) # create your choices in this method
def get_choices(optype, wsgroup):
# call your other methods here
return praep_button

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)

replacing field in queryset before return

I have the following view -
class DeployFilterView(generics.ListAPIView):
serializer_class = DefinitionSerializer
def get_queryset(self):
jobname = self.request.GET.get('jobname')
if jobname.count("\\") == 1:
jobname = jobname.replace("\\", "")
queryset = Jobmst.objects.db_manager('Admiral').filter(jobmst_name=jobname).exclude(jobmst_prntname__isnull=False, jobmst_dirty='X')
else:
parent, job = jobname.rsplit('\\', 1)
queryset = Jobmst.objects.db_manager('Admiral').filter(jobmst_prntname=parent, jobmst_name=job).exclude(jobmst_dirty='X')
return queryset
In that view there's a value field called "jobmst_runbook" which has a character that doesn't translate with the DRF XML renderer. What I'd like to be able to do is scan the queryset for a particular character - SOH or \u0001
If it finds this character I want to remove it before doing the return queryset
I solved this by doing the logic in my serializer. The serializer is now looking for the object that's causing the failure and stripping out the character.
class DefinitionSerializer(serializers.ModelSerializer):
runbook_url = serializers.SerializerMethodField('get_url')
# dependencies = serializers.RelatedField(many=True)
jobdep = serializers.HyperlinkedRelatedField(
source='jobdep_set', # this is the model class name (and add set, this is how you call the reverse relation of bar)
view_name='jobdep-detail' # the name of the URL, required
)
# triggers = serializers.RelatedField(many=True)
trgmst = serializers.HyperlinkedRelatedField(
source='trgmst_set', # this is the model class name (and add set, this is how you call the reverse relation of bar)
view_name='trgmst-detail' # the name of the URL, required
)
class Meta:
model = Jobmst
resource_name = 'jobmst'
depth = 2
fields = ('jobmst_id', 'jobmst_type', 'jobmst_prntid', 'jobmst_active', 'evntmst_id',
'jobmst_evntoffset', 'jobmst_name', 'jobmst_mode', 'jobmst_owner', 'jobmst_desc',
'jobmst_crttm', 'jobdtl_id', 'jobmst_lstchgtm', 'runbook_url', 'jobcls_id', 'jobmst_prntname',
'jobmst_alias', 'jobmst_dirty', 'job_dependencies', 'job_events')
def get_url(self, obj):
if obj.jobmst_runbook == None:
pass
else:
return force_text(obj.jobmst_runbook[:-5])

Setting choices and forms at runtime for a Form Wizard?

I am trying to use Form Wizard but I can't figure out where to set the choices for the fields.
#views.py
class QuizWizard(SessionWizardView):
def done(self, form_list, **kwargs):
return render_to_response('done.html', {
'form_data':[form.cleaned_data for form in form_list],
})
#forms.py
class QuestionForm(forms.ModelForm):
#selection = forms.ChoiceField()
class Meta:
model = Question
I see an empty form that looks like the admin panel for adding an object.
I would like to be able to pass a question to the form and have the question field filled out and not editable and preferable not submitted.
If I do
(r'^(?P<quiz_id>\d+)', QuizWizard.as_view(get_form_list)),
the function get_form_list has no length
(r'^(?P<quiz_id>\d+)', QuizWizard.as_view(get_form_list(quiz_id))),
Quiz_id is unknown.
so now I am trying to pass quiz_id to the view function and generate the list of question forms to be used in the form wizard
urls.py
url(r'^(?P<quiz_id>\d+)', 'quiz.views.get_form_list'),
views.py
class QuizWizard(SessionWizardView):
def done(self, form_list, **kwargs):
return render_to_response('done.html', {
'form_data':[form.cleaned_data for form in form_list],
})
def get_form_list(request, quiz_id):
quiz = Quiz.objects.get(id=quiz_id)
question_forms = []
for question in quiz.questions.all():
choices = []
for choice in question.choices.all():
choices.append(choice)
f = QuestionForm(instance=question)
question_forms.append(f)
return QuizWizard.as_view(question_forms)(request)
I am getting the error message
issubclass() arg 1 must be a class
Update based on Rohan's answer:
def get_form_list(request, quiz_id):
quiz = Quiz.objects.get(id=quiz_id)
question_forms = []
for question in quiz.questions.all():
choices = []
for choice in question.choices.all():
choices.append(choice)
f = QuestionForm(instance=question)
question_forms.append(f)
inst_dict = {}
for idx, question in enumerate(question_forms):
inst_dict[str(idx)] = question
print inst_dict
#inst_dict = { str(index(x)) : x for x in question_forms}
QuestFormList = []
for i in range(len(question_forms)):
QuestFormList.append(QuestionForm)
QuizWizard.as_view(QuestFormList, instance_dict=inst_dict)(request)
With this code I am getting an error
'ModelFormOptions' object has no attribute 'many_to_many'
Here is my models.py
class Choice(models.Model):
choice = models.CharField(max_length=64)
def __unicode__(self):
return self.choice
#create a multiple choice quiz to start
class Question(models.Model):
question = models.CharField(max_length=64)
answer = models.CharField(max_length=64)
choices = models.ManyToManyField(Choice)
module = models.CharField(max_length=64)
def __unicode__(self):
return self.question
class Quiz(models.Model):
name = models.CharField(max_length=64)
questions = models.ManyToManyField(Question)
def __unicode__(self):
return self.name
You should call it using the class instead of object. So change your call to
QuizWizard.as_view(question_forms)(request)
Update:
The wizard view takes form class list as parameters not the form instance. You are creating form instances in question_forms and passing it to view.
If you want to pass instance for the form in each step, you can pass instance_dict.
Something like ...
inst_dict = { '0': question_forms[0], #step 0 instance
'1': question_forms[1], #step 1 instance
}
QuestFormList = [QuestionForm, QuestionForm ...]
QuizWizard.as_view([QuestFormList, instance_dict=inst_dict)(request)