Django models class attribute and instance property? - python

I implement very simple hit-count models in Django.
models.py
from django.db import models
from model_utils.models import TimeStampedModel
from posts.models import Post
class PostHit(TimeStampedModel):
post = models.ForeignKey(Post, related_name='post_hits')
num_of_hit = models.IntegerField()
class Meta:
verbose_name_plural = "Post hits"
def __str__(self):
return self.post.title
def increase_hit(self):
self.num_of_hit += 1
views.py
from django.views.generic.detail import DetailView
from django.core.exceptions import ObjectDoesNotExist
from posts.models import Post, PostHit
from posts.forms import CommentForm
class PostDetailView(DetailView):
model = Post
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(**kwargs)
context['category'] = self.kwargs['category']
context['form'] = CommentForm()
context['tracking_hit_post'] = self.tracking_hit_post()
return context
def tracking_hit_post(self):
post = self.model.objects.get(pk=self.object.id)
post_hit = PostHit.objects.filter(post=post).first()
if post_hit:
post_hit.increase_hit()
else:
post_hit = PostHit.objects.create(
post=post,
num_of_hit=1
)
print(post_hit.num_of_hit)
return post_hit.num_of_hit
Once PostHit instance created, it calls increase_hit() everytime I visit DetailVie.
But it doesn't increase right way.
First it prints 1. And when I refresh the page, it prints 2. At next refresh, it prints 2 again. It doesn't increase anymore after 2.
What's wrong with my code? Did I misunderstand class attribute and instance property?

You need to save the model after updating it:
def increase_hit(self):
self.num_of_hit += 1
self.save()
Otherwise, your changes persist only for the lifetime of the object.

Related

Not able to get current logged in user in django model

I am trying to get current logged in user through my model so that I can only see the current user in my order page dropdown:
I have gone through a lot of documents which state that it is not that easy or feasible to get current logged in user in model.
I have tried other method like getting AUTH_USER_MODEL but it is returning admin level users as well so not solving the problem.
I am also sending the current logged in user from my views file but dont know how to access it inside form class, able to access it in init but dont know how it can be accessed in class.
models.py :
from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
from django.contrib.auth import get_user_model
from django.http import HttpResponse,HttpRequest
class Customer(models.Model):
name = models.CharField(max_length=200,null=True)
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
date_created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Order(models.Model):
product = models.ForeignKey(Product,null=True, on_delete=models.SET_NULL)
#customer = models.ForeignKey(settings.AUTH_USER_MODEL,null=True, on_delete=models.SET_NULL)
customer = models.ForeignKey(Customer,null=True, on_delete=models.SET_NULL)
date_ordered = models.DateTimeField(auto_now_add=True)
status = models.CharField(max_length=20,default= 'PENDING')
def __str__(self):
return str(self.customer.id)
forms.py :
class createorderform(ModelForm):
def __init__(self,*args,**kwargs):
self._instance=kwargs.pop('instance',None)
super().__init__(*args,**kwargs)
#def __init__(self,instance):
# self.user = instance
# super().__init__(instance)
class Meta:
model=Order
fields="__all__"
exclude=['status']
Views.py
def placeorder(request,i):
try:
products = Product.objects.all()
customer = Customer.objects.get(id=i)
print("Customer:",customer)
#form=createorderform(prod=products,cust=customer)
form=createorderform(instance=customer)
#form=createorderform()
if(request.method=='POST'):
form=createorderform(request.POST,instance=customer)
if(form.is_valid()):
form.save()
return redirect('/')
context={'form':form}
return render(request,'app_name/placeorder.html',context)
except:
print("Error occurred : {exec}".format(exec=traceback.format_exc()))
What I am getting is all the users:
What I want is to only show the current user in drop down.
Please help or guide me in the right direction.
Thanks in advance!!
Something like
from django.forms import ModelForm
from django.views.generic import CreateView
class CreateOrderForm(ModelForm):
class Meta:
model = Order
exclude = ['status', 'customer']
def __init__(self, **kwargs):
self.customer = kwargs.pop('customer')
super().__init__(**kwargs)
def save(self, commit=True):
self.instance.customer = self.customer
return super().save(commit=commit)
class PlaceOrderView(CreateView):
model = Order
form_class = CreateOrderForm
template_name = 'app_name/placeorder.html'
def get_form_kwargs(self):
return {
**super().get_form_kwargs(),
'customer': Customer.objects.get(user=self.request.user),
}
should be enough.
So I have kind of tried one solution and this one work, although I have tried it earlier but in between 100's of documents and solutions the easy one got lost :
In my forms.py :
class createorderform(ModelForm):
def __init__(self,*args,**kwargs):
self._instance=kwargs.pop('instance',None)
super().__init__(*args,**kwargs)
#def __init__(self,instance):
# self.user = instance
# super().__init__(instance)
class Meta:
model=Order
fields="__all__"
exclude=['customer','status']
I excluded the customer field and instead populated it in my views. py:
def placeorder(request,i):
try:
products = Product.objects.all()
customer = Customer.objects.get(id=i)
print("Customer:",customer)
#form=createorderform(prod=products,cust=customer)
form=createorderform(instance=customer)
#form=createorderform()
if(request.method=='POST'):
form=createorderform(request.POST,instance=customer)
if(form.is_valid()):
curr_user = form.save(commit=False)
curr_user.customer = customer
curr_user.save()
return redirect('/')
context={'form':form}
return render(request,'app_name/placeorder.html',context)
except:
print("Error occurred : {exec}".format(exec=traceback.format_exc()))
So I am overriding the save() and building my current logged in user in the view itself while saving it. It's storing the same user in db as well.
EDIT:
def placeorder(request,i):
try:
products = Product.objects.all()
customer = Customer.objects.get(id=i)
print("Customer:",customer)
#form=createorderform(prod=products,cust=customer)
form=createorderform()
#form=createorderform(instance=customer)
if(request.method=='POST'):
form=createorderform(request.POST)
#form=createorderform(request.POST,instance=customer)
if(form.is_valid()):
curr_user = form.save(commit=False)
curr_user.customer = customer
curr_user.save()
return redirect('/')
context={'form':form}
return render(request,'app_name/placeorder.html',context)
except:
print("Error occurred : {exec}".format(exec=traceback.format_exc()))
So I have change my form object and I am not sending my current logged in user to forms any more so just creating the object without "instance=customer" which got my current logged in user.
Now we don't need to handle the instance in init method of createorderform form :
class createorderform(ModelForm):
#def __init__(self,*args,**kwargs):
# self._instance=kwargs.pop('instance',None)
# super().__init__(*args,**kwargs)
#def __init__(self,instance):
# self.user = instance
# super().__init__(instance)
class Meta:
model=Order
fields="__all__"
exclude=['customer','status']

Get detail by Unique ID but not PK in Django Rest Framework URLs

I am trying to create Rest API using DRF. Wanted to get detail by using UniqueId. I can use PK and get the output but wanna use unique id (token_id in my jobs Model) created in the model field.
Models.py
from django.db import models
from rest_api.util import unique_slug_generator
from django.urls import reverse
# Create your models here.
class Jobs(models.Model):
token_id = models.CharField(max_length=64, unique=True)
name = models.CharField(max_length=100)
url = models.URLField()
environment = models.CharField(max_length=100, null=True)
runtestnow = models.BooleanField(default=False)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('token_id', kwargs={'token_id':self.token_id})
class Queue(models.Model):
tokenid = models.ForeignKey(Jobs, on_delete=models.CASCADE)
date = models.DateField(auto_now=True)
def __str__(self):
return self.tokenid
class VM(models.Model):
vm_count = models.IntegerField(default=120)
def __str__(self):
return f"VM Count: {self.vm_count}"
Urls.py
from django.urls import path, include
from . import views
from .views import (RegisterTestMethodView,
RegisterTestMethodViewDetail,
CheckStatusView,
ReleaseTokenView
)
from rest_framework import routers
from rest_framework.authtoken.views import obtain_auth_token
from rest_framework.urlpatterns import format_suffix_patterns
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
router = routers.DefaultRouter()
router.register('jobs', views.JobsView)
urlpatterns = [
path('', include(router.urls)),
path('registertestmethod/', RegisterTestMethodView.as_view()),
path('registertestmethod/<int:pk>/',
RegisterTestMethodViewDetail.as_view()),
path('checkstatus/<int:pk>', CheckStatusView.as_view()),
path('checkstatus/<token_id>', CheckStatusView.as_view()),
path('releasetoken/<int:pk>', ReleaseTokenView.as_view()),
]
Serializers.py
from rest_framework import serializers
from .models import Jobs
from django.utils.crypto import get_random_string
class JobsSerializers(serializers.HyperlinkedModelSerializer):
token_id = serializers.CharField(default=get_random_string(length=25))
class Meta:
model = Jobs
fields = ('id', 'name', 'url','runtestnow','token_id')
class CheckStatusSerializers(serializers.HyperlinkedModelSerializer):
class Meta:
model = Jobs
fields = ('id','runtestnow')
class RegisterTestMethodSerializers(serializers.HyperlinkedModelSerializer):
class Meta:
model = Jobs
fields = ('id', 'name', 'url', 'environment', 'runtestnow', 'token_id')
Views.py
from rest_framework import viewsets, permissions, authentication
from .models import Jobs, VM, Queue
from .serializers import (JobsSerializers,
RegisterTestMethodSerializers,
CheckStatusSerializers)
import json
import datetime
import collections
collections.deque()
#3rd time
from rest_framework import generics
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.authentication import (SessionAuthentication,
BasicAuthentication,
TokenAuthentication)
from rest_framework.permissions import IsAuthenticated
from django.utils.crypto import get_random_string
with open('greet/static/greet/static/config.json', 'r') as
data_config:
data_ready = json.load(data_config)
totalVM = int(data_ready['totalVM'])
max_vm = int(data_ready['max_vm_count'])
grid_name = (data_ready['GridNameForDev'])
min_vm = int(data_ready['min_vm_count'])
class RegisterTestMethodView(APIView):
# authentication_classes = [SessionAuthentication,
TokenAuthentication, BasicAuthentication]
# permission_classes = [IsAuthenticated] # No access (not even
read if not authorized)
def get(self, request):
snippets = Jobs.objects.all()
serializer = RegisterTestMethodSerializers(snippets,
many=True)
return Response(serializer.data)
def post(self, request):
queue = VM.objects.all()
id_token = get_random_string(length=25)
if not queue:
queue = VM(vm_count=totalVM)
queue.save()
else:
for queue_obj in queue:
queue = queue_obj
if queue.vm_count > min_vm:
queue.vm_count -= max_vm
queue.save()
request.data["token_id"] = id_token
request.data["runtestnow"] = True
else:
request.data["token_id"] = id_token
request.data["runtestnow"] = False
serializer = RegisterTestMethodSerializers(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({'TokenId': serializer.data['token_id'],
'RunTestNow': serializer.data['runtestnow'],
'VmCount': queue.vm_count,
'GridName': grid_name, 'Vm_left':
queue.vm_count}, status=status.HTTP_201_CREATED)
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
class JobsView(viewsets.ModelViewSet):
queryset = Jobs.objects.all()
serializer_class = JobsSerializers
lookup_field = 'token_id'
class CheckStatusView(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk, token_id):
try:
return Jobs.objects.get(pk=pk)
except Jobs.DoesNotExist:
raise Http404
def get(self, request, token_id):
pk = request.GET.get('pk')
print(pk)
queue = VM.objects.get()
job_list = Jobs.objects.exclude(runtestnow=True)
filtered = Jobs.objects.filter(id=pk)
next_q = job_list.order_by('id').first()
waitlist = 1
return Response(
{"tokenid": token_id, "Runtestnow": False, "VMcount":
queue.vm_count,
'GridName': grid_name, 'waitlist #': waitlist,
'Vm_left':
queue.vm_count}, status=status.HTTP_201_CREATED)
def post(self, request, pk):
queue = VM.objects.get()
vm_count = queue.vm_count
job_list = Jobs.objects.exclude(runtestnow=True)
filtered = Jobs.objects.filter(id=pk)
next_q = job_list.order_by('id').first()
waitlist = int(pk-next_q.id + 1)
if next_q:
print(next_q.id)
if next_q.id == pk and queue.vm_count > min_vm:
queue.vm_count -= max_vm
filtered.update(runtestnow=True)
queue.save()
vm_used = max_vm
else:
filtered.update(runtestnow=False)
queue.save()
vm_used = 0
snippet = self.get_object(pk)
serializer = RegisterTestMethodSerializers(snippet)
return Response({"tokenid": serializer.data["id"],
"Runtestnow": serializer.data['runtestnow'], "VMcount":
vm_used,
'GridName': grid_name, 'waitlist #': waitlist ,
'Vm_left': queue.vm_count},
status=status.HTTP_201_CREATED)
class ReleaseTokenView(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk):
try:
return Jobs.objects.get(pk=pk)
except Jobs.DoesNotExist:
raise Http404
def delete(self, request, pk, format=None):
queue = VM.objects.get()
if not queue:
queue = VM(vm_count=totalVM)
if not self.get_object(pk):
print("Not Method Called...")
return
if queue.vm_count < totalVM :
queue.vm_count += max_vm
queue.save()
elif queue.vm_count + max_vm > totalVM:
queue.vm_count = totalVM
queue.save()
snippet = self.get_object(pk)
snippet.delete()
return Response(data={'Released': True},
status=status.HTTP_204_NO_CONTENT)
I can get information using but I wanna user token_id. I can do that using Serializers as it is in jobs.
If I do
localhost/jobs/xJcn8XxF2g9DmmwQwGS0Em754. # --> I get the output but I
# wanna use and I am aware
#that this will return all CRUD methods but how do I apply the
#business logic in Serializers.
localhost/checkstatus/xJcn8XxF2g9DmmwQwGS0Em754 . # --> I wanna
# apply business logic before getting the output. Which
# returns Response related to the PK as well.
What is the best way to do it?
Do I add it on serializer.py(how) or views.py?
I would appreciate it if you provide any helpful documents.
You should set lookup_field as token_id in your serializer and viewset.
Here is answer Django Rest Framework: Access item detail by slug instead of ID
Actually I was able to do it by some research. It seems like I have to pass a unique id (token_id) in URL and query using the same unique id (token_id) on the views.py. I was aware that there is modelviewset that does it effortlessly as mentioned by Ishak, but I wanted to use APIView and on top of that I wanted some business logic to be added. I probably have to do some more research on how to add logic to modelviewset. Here is my Solution.
Views.py
def get(self, request, token_id):
get_job = Jobs.objects.get(token_id=token_id)
pk = get_job.id
job_list = Jobs.objects.exclude(runtestnow=True)
next_q = job_list.order_by('id').first()
queue = VM.objects.get()
waitlist = int(pk) - int(next_q.id)
if waitlist == 1:
waitlist = 'You are next on the queue. :)'
return Response(
{"tokenid": token_id, "Runtestnow": False, "VMcount":
queue.vm_count,
'GridName': grid_name, 'waitlist #': waitlist, 'Vm_left':
queue.vm_count}, status=status.HTTP_201_CREATED)
Urls.py
path('checkstatus/<token_id>', CheckStatusView.as_view()),
We can always use the slug field, but I really wanted token_id as input. This should work fine for me as of now.
There might be some other way as well. Feel free to share.

How to place search query on url on django?

I am creating a search application with Django.
I made an article model and a Feedback model that records the rating of articles.
After entering search box and displaying the search results, click one of the results then goes to the detail screen.
After selecting feedback on the detail screen and pressing the submit button, I want to save a search query to the feedback model.
I think that solution is to add a query in the URL like portal/search/?=query and read it, but I don't know how to code it. Also, could you teach me if there is an implementation method other than reading query in the URL?
Also, when I go back from the detail screen, I want to display the previous search results too.
Please comment if you have any questions.
Forgive for my poor English.
models.py
from django.db import models
from django.urls import reverse
from taggit.managers import TaggableManager
class KnowHow(models.Model):
BASIC_TAGS =(
('1','one'),
('2','two'),
('3','three'),
('4','four'),
('5','five'),
('6','six'),
)
CATEGORY =(
('1','Type2'),
('2','Type1'),
)
author = models.ForeignKey('auth.User',on_delete=models.CASCADE)
category = models.CharField(max_length=1,choices=CATEGORY,default='1')
title = models.CharField(max_length=200)
text = models.TextField(blank=True,default=' ')
# delault=' ':import system will give a error if text column is null
file = models.FileField(blank=True,upload_to='explicit_knowhows')
basic_tag = models.CharField(max_length=1,choices=BASIC_TAGS,default='1')
free_tags = TaggableManager(blank=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('portal:index')
class Feedback(models.Model):
EFFECT =(
('1','great'),
('2','maybe good'),
('3','bad'),
)
NOVEL =(
('1','I didn't know that'),
('2','I know, but I forgot'),
('3','I know this.'),
)
kh = models.ForeignKey(KnowHow, on_delete=models.PROTECT)
user = models.ForeignKey('auth.User',on_delete=models.CASCADE)
query = models.TextField(blank=True)
time = models.DateTimeField(auto_now_add=True)
efficacy = models.CharField(max_length=1,choices=EFFECT,default='1')
novelty = models.CharField(max_length=1,choices=NOVEL,default='1')
def __str__(self):
return self.time.strftime("%Y/%m/%d %H:%M:%S")
views.py
from django.urls import reverse, reverse_lazy
from django.http import HttpResponse
from django.views import generic
from django.views.generic.edit import ModelFormMixin
from django.shortcuts import redirect,get_object_or_404
from django.core.exceptions import PermissionDenied
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from .models import KnowHow
from taggit.models import Tag
from .forms import SearchForm,FeedbackForm
from django.db.models import Q
"""
Django Auth
The LoginRequired mixin
https://docs.djangoproject.com/en/2.0/topics/auth/default/#the-loginrequired-mixin
The login_required decorator
https://docs.djangoproject.com/en/2.0/topics/auth/default/#the-login-required-decorator
#login_required
"""
class IndexView(LoginRequiredMixin,generic.list.ListView):
model = KnowHow
#paginate_by = 5
ordering = ['-title']
# template_name = 'portal/KnowHow_list.html'
class DetailView(ModelFormMixin,LoginRequiredMixin,generic.detail.DetailView):
# from https://torina.top/detail/337/
model = KnowHow
form_class = FeedbackForm
template_name = 'portal/KnowHow_detail.html'
def form_valid(self, form):
kh_pk = self.kwargs['pk']
Feedback = form.save(commit=False)
Feedback.kh = get_object_or_404(KnowHow, pk=kh_pk)
Feedback.query=""
Feedback.user=self.request.user
Feedback.save()
return redirect('portal:search')
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
self.object = self.get_object()
return self.form_invalid(form)
class CreateView(LoginRequiredMixin, generic.edit.CreateView): # The LoginRequired mixin
model = KnowHow
fields = ['category','title','text','file','basic_tag','free_tags']
#template_name = 'portal/KnowHow_form.html'
def form_valid(self, form):
# This method is called when valid form data has been posted.
# It should return an HttpResponse.
# https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-editing/#models-and-request-user
form.instance.author = self.request.user
return super(CreateView, self).form_valid(form)
class UpdateView(LoginRequiredMixin, generic.edit.UpdateView): # The LoginRequired mixin
model = KnowHow
fields = ['category','title','text','file','basic_tag','free_tags']
#template_name = 'portal/KnowHow_form.html'
class DeleteView(LoginRequiredMixin, generic.edit.DeleteView): # The LoginRequired mixin
model = KnowHow
success_url = reverse_lazy('portal:index')
def delete(self, request, *args, **kwargs):
result = super().delete(request, *args, **kwargs)
Tag.objects.filter(knowhow=None).delete()
return result
#template_name = 'portal/KnowHow_confirm_delete.html'
class SearchIndexView(LoginRequiredMixin, generic.ListView):
template_name="search/search_index.html"
model = KnowHow
def post(self, request, *args, **kwargs):
form_value = [
self.request.POST.get('basic_tag', None),
self.request.POST.get('free_tags', None),
]
request.session['form_value'] = form_value
self.request.GET = self.request.GET.copy()
self.request.GET.clear()
return self.get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
basic_tag = ''
free_tags = ''
if 'form_value' in self.request.session:
form_value = self.request.session['form_value']
basic_tag = form_value[0]
free_tags = form_value[1]
default_data = {'basic_tag': basic_tag,
'free_tags': free_tags,
}
test_form = SearchForm(initial=default_data)
context['test_form'] = test_form
return context
def get_queryset(self):
if 'form_value' in self.request.session:
form_value = self.request.session['form_value']
basic_tag = form_value[0]
free_tags = form_value[1]
condition_basic_tag = Q()
condition_free_tags = Q()
if len(basic_tag) != 0 and basic_tag[0]:
condition_basic_tag = Q(basic_tag=basic_tag)
if len(free_tags) != 0 and free_tags[0]:
condition_free_tags = Q(free_tags__name__in=free_tags)
return KnowHow.objects.filter(condition_basic_tag & condition_free_tags).distinct()
else:
return KnowHow.objects.none()
#login_required
def help(request):
return HttpResponse("Member Only Help Page")
urls.py
from django.urls import path
from . import views
# set the application namespace
# https://docs.djangoproject.com/en/2.0/intro/tutorial03/
app_name = 'portal'
urlpatterns = [
# ex: /
path('', views.IndexView.as_view(), name='index'),
# ex: /KnowHow/create/
path('KnowHow/create/', views.CreateView.as_view(), name='create'),
# ex: /KnowHow/1/
path('KnowHow/<int:pk>/detail/', views.DetailView.as_view(), name='detail'),
# ex: /KnowHow/1/update/
path('KnowHow/<int:pk>/update/', views.UpdateView.as_view(), name='update'),
# ex: /KnowHow/1/delete
path('KnowHow/<int:pk>/delete/', views.DeleteView.as_view(), name='delete'),
# ex: /KnowHow/help/
path('KnowHow/help/', views.help, name='help'),
path('search/',views.SearchIndexView.as_view(), name='search')
]
There are several solutions for your problem.
First one is the exact solution you mentioned yourself. using a query string parameter like ?q= for KnowHow details view.
Using a SearchLog model and using that model's identifier. When someone hits the /search/ endpoint, you create a new SearchLog and pass the pk for this record to your front. Basically it would be just like ?q= option. instead you can use ?search_id= to bind the feedback to an specific SearchLog
Use user sessions. Bind the searched query to user's session and when they want to create a new Feedback use the query in their session.
For the first two options, you just need to create your urls for the detail links properly (in your search result page). In your template, do something like below:
# You are probably doing something like this
{% for r in results %}
{{r.name}}
{% endfor %}
# You should do this instead
{% for r in results %}
{{r.name}}
{% endfor %}
You can either pass the current_query in your context when rendering the template, or use javascript to get that value from browser's location / query string.
I changed get_context_data function in SearchIndexView to this:
in the last line before return add these two lines
context['basic_tag'] = basic_tag
context['free_tags'] = free_tags
And I changed html too.
{{ KnowHow.title }}
Thanks, #n1ma

Django-haystack generic SearchView - no results

I'm tryinig to get haystack working with a class-based generic view according to the documentation here. I can get results from a SearchQuerySet in the shell, so the models are being indexed. But I can't get the view to return a result on the page.
The main reason for using the generic view is that I want to extend later with more SQS logic.
I'm probably missing something obvious...
views.py :
from haystack.query import SearchQuerySet
from haystack.generic_views import SearchView
from .forms import ProviderSearchForm
from .models import Provider
class ProviderSearchView(SearchView):
template_name = 'search/provider_search.html'
form_class = ProviderSearchForm
def get_context_data(self, *args, **kwargs):
""" Extends context to include data for services."""
context = super(ProviderSearchView, self).get_context_data(*args, **kwargs)
context['body_attr'] = 'id="provider-search"'
return context
def get_queryset(self):
queryset = super(ProviderSearchView, self).get_queryset()
return queryset.filter(is_active=True)
search_indexes.py:
from haystack import indexes
from .models import Provider
class ProviderIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
title = indexes.CharField(model_attr='name')
created = indexes.DateTimeField(model_attr='created')
def get_model(self):
return Provider
def index_queryset(self, using=None):
"Used when the entire index for model is updated."
return self.get_model().objects.all()
forms.py
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, Submit
from crispy_forms.bootstrap import FieldWithButtons
from haystack.forms import SearchForm
from .models import Provider
class ProviderSearchForm(SearchForm):
""" Override the form with crispy styles """
models = [ Provider ]
def __init__(self, *args, **kwargs):
super(ProviderSearchForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.disable_csrf = True
self.helper.form_tag = False
self.helper.form_show_labels = False
self.helper.layout = Layout (
FieldWithButtons(
Field('q', css_class='form-control input-lg', placeholder="Search providers..."),
Submit('','Search', css_class='btn btn-lg btn-primary'))
)
def get_models(self):
return self.models
def search(self):
sqs = super(ProviderSearchForm, self).search().models(*self.get_models())
return sqs
def no_query_found(self):
return self.searchqueryset.all()
The problem was that my page template was using the wrong variable in the for loop.
The documentation suggests:
for result in page_object.object_list
It should be:
for result in page_obj.object_list
note the template variable is page_obj.
See issue post on GitHub

How do I add together fields from a manytomanyfield in django?

I am creating a quote-generator in Django. I want to calculate the total of all items, insert it into a field, and save it.
The models are as follows:
from django.db import models
from django.core.urlresolvers import reverse
class Product(models.Model):
product_name = models.CharField(max_length=50)
product_description = models.CharField(max_length=200)
product_price = models.IntegerField(max_length=4)
def __unicode__(self):
return self.product_name
class Meta:
ordering = ('product_name',)
class Quote(models.Model):
quotee_name = models.CharField("Name", max_length=40)
quotee_email = models.EmailField("Email")
quotee_phone = models.IntegerField("Phone", max_length=10)
quotee_products = models.ManyToManyField(Product, verbose_name="Products")
quotee_total = models.IntegerField("Estimate", max_length=10, null=True, blank=True)
def __unicode__(self):
return self.quotee_email
class Meta:
ordering = ('quotee_email',)
def get_absolute_url(self):
return reverse('quote-detail', kwargs={'pk': self.pk, })
I am not using this through the Admin, so here is the forms.py:
from django import forms
from django.forms import CheckboxSelectMultiple
from InternalDusettenet.apps.quotes.models import Quote
class QuoteForm(forms.ModelForm):
class Meta:
model = Quote
fields = ('quotee_name', 'quotee_email', 'quotee_phone',
'quotee_products')
widgets = {
'quotee_products': CheckboxSelectMultiple(attrs={'size': 10}),
}
And here is the views.py file. I have it set to just save a '1' into the form so that it actually saves. What I want is to replace the '1' with a function that returns the value of 'Product.product_price' for every one selected in 'Quote.quotee_products'. When I create a quote, I select the products, and it gives me the sum of all selected 'product_price' fields related to the selected products:
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.generic import ListView, DetailView
from django.shortcuts import Http404, get_object_or_404
from InternalDusettenet.apps.quotes.models import Quote
from InternalDusettenet.apps.quotes.forms import QuoteForm
class QuoteCreate(CreateView):
model = Quote
template_name = "quotes/quote_create_edit.html"
fields = ['quotee_name', 'quotee_email', 'quotee_phone',
'quotee_products']
form_class = QuoteForm
def form_valid(self, form):
form.instance.quotee_total = 1
return super(QuoteCreate, self).form_valid(form)
class QuoteList(ListView):
model = Quote
template_name = "quotes/quote_list.html"
class QuoteDetail(DetailView):
model = Quote
template_name = "quotes/quote_detail.html"
class QuoteUpdate(UpdateView):
model = Quote
template_name = "quotes/quote_create_edit.html"
fields = ['quotee_name', 'quotee_email', 'quotee_phone',
'quotee_products', 'quotee_total']
form_class = QuoteForm
class QuoteDelete(DeleteView):
model = Quote
success_url = '/'
template_name = "quotes/quote_delete.html"
I have read the Django docs MANY times but I have no clue how to do this one simple thing.
I am using Django 1.7 and Python 2.7.
No reason to save it in the database, just make it a method or property of the Quote object:
class Quote(models.Model):
...
def quotee_total(self):
return self.quotee_products.aggregate(total=models.Sum('product_price'))['total']
If need be, you can cache the value and fill the cache on the initial query:
class Quote(models.Model):
...
def quotee_total(self):
if not hasattr(self, '_quotee_total'):
self._quotee_total = self.quotee_products.aggregate(total=models.Sum('product_price'))['total']
return self._quotee_total
quotes = Quote.objects.annotate(_quotee_total=models.Sum('quotee_products__product_price'))
You can of course save that value in the database, but there's little reason. If you're worried about performance, that is better handled with caching than with saving the value to the database.
I would not calculate the total in a view. This makes more sense as a method.
class Quote(models.Model):
def calculate_quotee_total(self):
return sum(product.product_price for product in self.quotee_products.all())
def __save__(self):
self.quotee_total = self.calculate_quotee_total()
super(Quote, self).save()
Quote.quotee_total could also be calculated as needed, instead of saving it in the db.
class Quote(models.Model):
#property
def quotee_total(self):
return sum(product.product_price for product in self.quotee_products.all())

Categories