django request.GET in models - django

Is it possible in Django to have models method with request.GET ?
e.g.
class Car(models.Model):
owner = ForeignKey(Owner)
car_model = ...
def car_filter(self, request):
query = request.GET.get("q")
if query:
Car.objects.filter(owner = self.id.order_by('id')
else:
Car.objects.filter(owner = me).order_by('id'
)
?

Purely technically speaking, sure, you can - as long as you can pass the request object from the view. The example code you've posted is syntactically incorrect, but, something like this is technically possible. You just have to make sure that the method is class-method, not instance-method one (since you don't have any instances in this case):
class Car(models.Model):
...
#classmethod
def get_by_owner(cls, request):
query = request.GET.get("q")
if query:
return cls.objects.filter(owner=query)
elif request.user.is_authenticated():
return cls.objects.all()
def your_view(request):
cars = Car.get_by_owner(request)
...
However, DON'T DO THIS. It's a bad idea because you're moving your request processing logic to a model. Models should only care about the data, and user request handling is view's job.
So, I'd suggest to have all the logic in the views:
def your_view(request):
cars = Car.objects.all().order_by("id")
query = request.GET.get("q")
if query:
cars = cars.filter(owner=query)
...
If you need some complicated logic, that a lot of views would share, you can use model managers:
class CarManager(model.Manager):
def owned(self, username=None):
queryset = super(CarManager, self).get_query_set()
if username:
user = Owner.objects.get(username=username)
queryset = queryset.filter(owner=user)
return queryset
class Car(models.Model):
...
objects = CarManager()
...
def your_view(request):
query = request.GET.get("q")
cars = Car.objects.owned(query)
...

Possible, but you have to pass the request manually:
# inside your views
qs_ = car_object.car_filter(request)
but I dont see any sense in doing so.
Everything that has to do with request should go into views which is the place for request-response flow.

Actually you can handle this stuff in your view only
def yourview(self, request):
query = request.GET.get("q")
if query:
Car.objects.filter(owner = self.id).order_by('id')
else:
Car.objects.filter(owner = me).order_by('id')
else other wise you have to send your request object to the model function from your view.

Related

HttpResponse error django generic template

I am able to render class based view generic ListView template using parameter hard coded in views.py.
class ResourceSearchView(generic.ListView):
model = creations
context_object_name = 'reviews'
template_name = 'reviews.html'
query = 'theory'
# def get(self, request):
# if request.GET.get('q'):
# query = request.GET.get('q')
# print(query)
queryset = creations.objects.filter(narrative__contains=query).order_by('-post_date')
However, when parameter is sent via form by GET method (below),
class ResourceSearchView(generic.ListView):
model = creations
context_object_name = 'reviews'
template_name = 'reviews.html'
query = 'theory'
def get(self, request):
if request.GET.get('q'):
query = request.GET.get('q')
print(query)
queryset = creations.objects.filter(narrative__contains=query).order_by('-post_date')
I receive this error
The view creations.views.ResourceSearchView didn't return an
HttpResponse object. It returned None instead.
Note that the parameter name q and associated value is being retrieved successfully (confirmed using print(query)).
So with CBV in Django, you have to return some kind of valid response that the interpreter can use to perform an actual HTTP action. Your GET method isn't returning anything and that's what is making Django angry. You can render a template or redirect the user to a view that renders a template but you must do something. One common pattern in CBV is to do something like:
return super().get(request, *args, **kwargs)
...which continues up the chain of method calls that ultimately renders a template or otherwise processes the response. You could also call render_to_response() directly yourself or if you're moving on from that view, redirect the user to get_success_url or similar.
Have a look here (http://ccbv.co.uk) for an easy-to-read layout of all the current Django CBVs and which methods / variables they support.
Thanks for the responses. Here is one solution.
class ResourceSearchView(generic.ListView):
model = creations
context_object_name = 'reviews'
template_name = 'reviews.html'
def get_queryset(self):
query = self.request.GET.get('q')
queryset = creations.objects.filter(narrative__contains=query).order_by('-post_date')
return queryset

How can I prefetch related objects in Django?

Assuming I have the following model with related methods:
class Turbine(models.Model):
...
pass
def relContracts(self):
contracts = self.contracted_turbines.all()
return contracts
class Contract(models.Model):
turbines = models.ManyToManyField(Turbine,related_name='contracted_turbines')
def _contracted_windfarm_name(self):
windfarms = self.turbines.order_by().values_list("wind_farm__name", flat=True).distinct().select_related
if len(windfarms) == 1:
return windfarms[0]
else:
return ", ".join([str(x) for x in windfarms])
contracted_windfarm_name = property(_contracted_windfarm_name)
def _turbine_age(self):
first_commisioning = self.turbines.all().aggregate(first=Min('commisioning'))['first']
start = self.start_operation.year
age = start - first_commisioning.year
return age
turbine_age = property(_turbine_age)
Django-debug-toolbar tells me, that the functions "_contracted_windfarm_name" and "_turbine_age" result in database duplicates for each contract.
My contracts queryset is received by the following get_queryset method where I already prefetched 'turbines' for other methods succesfully:
def get_queryset(self, **kwargs):
qs = super(ContractTableView, self).get_queryset().filter(active=True).prefetch_related('turbines', 'turbines__wind_farm')
self.filter = self.filter_class(self.request.GET, queryset=qs)
return self.filter.qs
I tried prefetching 'turbines__contracted_turbines' without being able to reduce the number of duplicates.
The _contracted_windfarm_name method is used to populate a column of a django-tables2 method as follows:
contracted_windfarm = dt2.Column(accessor='contracted_windfarm_name', verbose_name='Wind Farm', orderable=False)
Where am I mistaking? How can I prefetch the associated contracts of a turbine?
SOLUTION: First problem
I added a simple annotation to the queryset within the get_queryset() method:
def get_queryset(self, **kwargs):
qs = super(ContractTableView, self).get_queryset()\
.filter(active=True).prefetch_related('turbines', 'turbines__wind_farm')\
.annotate(first_com_date=Case(When(turbines__commisioning__isnull=False, then=Min('turbines__commisioning'))))
self.filter = self.filter_class(self.request.GET, queryset=qs)
return self.filter.qs
This leads to a slight change in the _turbine_age() method:
def _turbine_age(self):
first_commisioning = self.first_commisioning
start = self.start_operation.year
age = start - first_commisioning.year
return age
turbine_age = property(_turbine_age)
SOLUTION: second problem
With the turbines__wind_farm being prefetched in the get_queryset() method, there is no need to call the distinct() method:
def _contracted_windfarm_name(self):
windfarms = list(set([str(x.wind_farm.name) for x in self.turbines.all()]))
if len(windfarms) == 1:
return windfarms[0]
else:
return ", ".join([str(x) for x in windfarms])
contracted_windfarm_name = property(_contracted_windfarm_name)
All duplicated queries could be removed!
Thanks to #dirkgroten for his valuable contributions!
from django.db.models import Min
class ContractManager(models.Manager):
def with_first_commissioning(self):
return self.annotate(first_commissioning=Min('turbines__commissioning'))
class Contract(models.Model):
objects = ContractManager()
...
then Contract.objects.with_first_commissioning() returns you a Queryset with the additional first_commissioning value for each Contract. So in Contract._turbine_age() you can just remove the first line.
Now the windfarm names case is a bit more complex. If you're using Postgresql (which supports StringAgg) you could similarly add in your ContractManager this queryset:
from django.db.models import Subquery, OuterRef
from django.contrib.postgres.aggregates import StringAgg
def with_windfarms(self):
wind_farms = WindFarm.objects.filter('turbines__contract'=OuterRef('pk')).order_by().distinct().values('turbines__contract')
wind_farm_names = wind_farms.annotate(names=StringAgg('name', delimiter=', ')).values('names')
return self.annotate(wind_farm_names=Subquery(wind_farm_names))
then in your _contracted_windfarm_name() method, you can access self.wind_farm_names assuming you're looping through the results of the queryset (you should probably check with hasattr in case your method gets used in a different way).
If you're not on Postgresql, then just change the queryset to perform a prefetch_related and then make sure you don't add any query-related logic after that:
from django.db.models import Prefetch
def with_windfarms(self):
return self.prefetch_related(Prefetch('turbines', queryset=Turbine.objects.order_by().select_related('wind_farm').distinct('wind_farm__name')))
so that in your _contracted_wind_farms method, you can do [str(x.wind_farm.name) for x in self.turbines]
In both cases, I'm assuming somewhere in your views you loop through the contracts in the queryset:
for contract in Contract.objects.with_first_commissioning():
contract._turbine_age()...
for contract in Contract.objects.with_windfarms():
contract._contracted_windfarm_name()...

django-rest save array of data to db

hi im trying to save a form data into db.
i provided print of requset.data for you as you see requirement have two items.
i want to save each item in database i used for loop to save each item of list but the loop will save each character of item like h-e-l,... in table row...
where is my mistake ... thanks
also print of request.data.get('requirement') will retun second item
this is print of request.data in sever:
<QueryDict: {'requirement': ['hello', 'bye'], 'audience': ['adasd'], 'achievement': ['asdasd'], 'section': ['410101010'], 'title': ['asdasd'], 'mini_description': ['asdad'], 'full_description': ['asdasd'], 'video_length': ['10101'], 'video_level': ['P'], 'price': [''], 'free': ['true'], 'image': [<InMemoryUploadedFile: p.gif (image/gif)>]}>
view:
class StoreCreateAPIView(generics.CreateAPIView):
parser_classes = (MultiPartParser, FormParser)
permission_classes = [IsAuthenticated]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
def post(self, request, *args, **kwargs):
if request.method == 'POST':
print(request.data)
file_serial = ProductSerializer(data=request.data, context={"request": request})
if file_serial.is_valid():
file_serial.save(author_id=request.user.id)
requirement = request.data['requirement']
audience = request.data.get('audience')
achievement = request.data.get('achievement')
sections = request.data.get('section')
print(request.data['requirement'])
pid = file_serial.data.get('product_id')
for item in requirement :
req = ProductRequiredItems(
item = item,
product_id = pid
)
req.save()
First of all, overriding CreateAPIView's post method in your code makes your custom perform_create method useless, unless you explicitly call it from within your customized post method. Otherwise it will never be called.
also print of request.data.get('requirement') will retun second item
It does return the last item as per Django docs for QueryDict.__getitem__(key).
i want to save each item in database i used for loop to save each item of list but the loop will save each character of item like h-e-l,...
This is because of the above functionality of QueryDict. When you do:
requirement = request.data['requirement']
# requirement = request.__getitem__('requirement')
it will call QueryDict.__getitem__(key) method and thus return only the last item (which is string in you example).
Answer:
You can simply override CreateAPIView's create method, and let your serializer handle all the rest.
# views.py
from django.shortcuts import render
from rest_framework import generics, status
from rest_framework.response import Response
from .models import MyObj
from .serializers import MyObjSerializer
class MyObjView(generics.CreateAPIView):
serializer_class = MyObjSerializer
queryset = MyObj.objects.all()
def create(self, request, *args, **kwargs):
# The QueryDicts at request.POST and request.GET will be immutable
# when accessed in a normal request/response cycle.
# To get a mutable version you need to use QueryDict.copy().
req_data = request.data.copy()
requirements = req_data.pop('requirement')
serializers_data = []
for requirement in requirements:
req_data ['requirement'] = requirement
serializer = self.get_serializer(data=req_data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
serializers_data.append(serializer.data)
return Response(serializers_data, status=status.HTTP_201_CREATED)
# serializers.py
from rest_framework import serializers
from .models import MyObj
class MyObjSerializer(serializers.ModelSerializer):
class Meta:
model = MyObj
fields = '__all__'
Have a look at DRF CreateModelMixin. It defines create & perform_create methods that are used used in CreateAPIView upon executing POST request. I just altered them slightly to handle your specific case.
Hope it helps.

Django REST, Accessing methods of the selected object

this part of my code fills the queryset with [category_object].subcats.all(). let subcats be a method of category object:
serializer:
class CatSrlz(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('id', 'label', )
View:
class CatsViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Category.objects.filter(parent=None)
serializer_class = CatSrlz
def retrieve(self, request, *args, **kwargs):
# return Response({'res': self.kwargs})
queryset = Category.objects.get(pk=str(self.kwargs['pk'])).subCats.all()
dt = CatSrlz(queryset, many=True)
return Response(dt.data)
and url:
router.register(r'cats', views.CatsViewSet)
it works but i'm pretty sure that there must be a more correct way of doing so
Is there one?
thanks
When retrieving a single object, you can use the get_object method in your view, which look like this in DRF without modifications :
def get_object(self):
"""
Returns the object the view is displaying.
You may want to override this if you need to provide non-standard
queryset lookups. Eg if objects are referenced using multiple
keyword arguments in the url conf.
"""
queryset = self.filter_queryset(self.get_queryset())
# Perform the lookup filtering.
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
assert lookup_url_kwarg in self.kwargs, (
'Expected view %s to be called with a URL keyword argument '
'named "%s". Fix your URL conf, or set the `.lookup_field` '
'attribute on the view correctly.' %
(self.__class__.__name__, lookup_url_kwarg)
)
filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
obj = get_object_or_404(queryset, **filter_kwargs)
# May raise a permission denied
self.check_object_permissions(self.request, obj)
return obj
So you could adapt the part where you get your object :
filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
obj = get_object_or_404(queryset, **filter_kwargs)
and add your subcat logic there.
By the way, I don't get why you are using
dt = CatSrlz(queryset, many=True)
Shouldn't "retrieve" return a single object?

Django: How to make a query for on object based on an M2M field (multiple selections for field on search form)

I need help coming up with an efficient way to do a search query for a set of objects, based on a M2M field. My search form is going to look something like Blue Cross Blue Shield's | eg: this image
Now, let's suppose my model looks like this:
# models.py
class Provider(models.Model)
title = models.CharField(max_length=150)
phone = PhoneNumberField()
services_offered = models.ManyToManyField(ServiceType)
def __unicode__(self):
return self.title
class ServiceCategory(models.Model):
service_category = models.CharField(max_length=30)
def __unicode__(self):
return self.service_category
class Meta(object):
verbose_name_plural = "Service Categories"
class ServiceType(models.Model):
service_type = models.CharField(max_length=30)
service_category = models.ForeignKey(ServiceCategory)
def __unicode__(self):
return u'%s | %s' % (self.service_category, self.service_type
Also, we have to keep in mind that the options that we select are subject to change, since how they display on the form is dynamic (new ServiceCategories and ServiceTypes can be added at anytime). *How should I go about constructing a query for the Provider objects, given that a person using the search form can select multiple Services_Offered?*
This is currently my HIGHLY INEFFICIENT METHOD:
#managers.py
from health.providers.models import *
from django.db.models import Q
class Query:
def __init__(self):
self.provider_objects=Provider.objects.all()
self.provider_object=Provider.objects
self.service_object=ServiceType.objects
self.category_objects=ServiceCategory.objects.all()
def simple_search_Q(self, **kwargs): #matt's learning note: **kwargs passes any dictionary
return self.provider_objects.filter(
Q(services_offered__service_type__icontains=kwargs['service']),
Q(title__icontains=kwargs['title']),
Q(state=kwargs['state']),
).distinct().order_by('title')
====================
#views.py
from django.shortcuts import render_to_response
from health.providers.models import *
from health.search.forms import *
from health.search.managers import Query #location of the query sets
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.template import RequestContext
def simple_search(request):
if request.method == 'POST':
SimpleSearch_form = SimpleSearch(request.POST)
if SimpleSearch_form.is_valid():
request.session["provider_list"] = None
kwargs = {'title': request.POST['title'],
'service': request.POST['service'], 'state': request.POST['state'] }
provider_list = Query().simple_search_Q(**kwargs)
return pagination_results(request, provider_list)
else:
SimpleSearch_form = SimpleSearch()
return render_to_response('../templates/index.html', { 'SimpleSearch_form': SimpleSearch_form},
context_instance=RequestContext(request))
How can I make my query:
Obtain Provider objects based on selecting multiple request.POST['service']
More efficient
Thanks for any help in advanced.
Best Regards,
Matt
1: for multiple request.POST['service'], I assume you mean these are CheckBoxes.
I'd make the CheckBox values ID's, not names, and do a PK lookup.
'services_offered__pk__in': request.POST.getlist('service')
That would return all Provider objects that have ALL of the services selected.
PS: You are also using CapitalCase for instances which is very confusing. If you want your code to be readable, I highly recommend some changes to your style (don't use CapitalCase for instances or variables) and make your variables more descriptive.
SimpleSearch_form = SimpleSearch() # what is SimpleSearch?
simplesearch_form = SimpleSearchForm() # now, it's very clear what the class SimpleSearchForm is
# and the form instance is clearly a for instance.
2: making it more efficient? You could get rid of a lot of code and code separation by remove your whole Query class. Also, I don't know why you are using Q objects since you are not doing anything that would require it (like OR or OR + AND).
def simple_search(request):
if request.method == 'POST':
searchform = SimpleSearchForm(request.POST)
if searchform.is_valid():
request.session['provider_list'] = None
post = request.POST
providers = Provider.objects.filter(services_offered__pk__in=post.getlist('services'),
title=post['title'], state=post['state'])
return pagination_results(request, provider_list)
else:
searchform = SimpleSearchForm()
return direct_to_template(request, '../templates/index.html', { 'searchform': searchform})