Paginating the response of a Django POST request - python

I'm using Django for my work project. What I need to get is all likes put by some user. Likes are put to transactions, so I am getting the list of all transactions in 'items' field, where the user put his like/dislike. There are some optional parameters that can be in the request:
{
"user_id": (usually is taken from session but can be used to get likes of specific user by admins),
"like_kind_id": (1 for like/2 for dislike),
"include_code_and_name": (True/False),
"page_size": (integer number) for pagination
}
With the last parameter, I am facing the issues. The result should look like this:
{
​ "user_id":id,
​ "likes":[
​ {
"like_kind":{
"id":id,
"code":like_code,
"name":like_name,
},
"items":[
{
"transaction_id":transaction_id,
"time_of":date_created,
},...
]
}
]
}
"Likes" is just array of 2 elements (like and dislike).
So, my problem is that I cannot set pagination for "items" array as there can be a lot of transactions. I am getting the page_size from request.data.get("page_size") but cannot apply it so that the results will be paginated. I know that for GET-requests I need simply set pagination_class in views.py. Anyone knows how can I do the similar procedure for POST-request and set the page_size of pagination class to 'page_size' from data.request? =)
This is my views.py:
class LikesUserPaginationAPIView(PageNumberPagination):
page_size = 1
page_size_query_param = 'page_size'
max_page_size = 1
class LikesUserListAPIView(APIView):
"""
All likes of the user
"""
permission_classes = [IsAuthenticated]
authentication_classes = [authentication.SessionAuthentication,
authentication.TokenAuthentication]
pagination_class = LikesUserPaginationAPIView
#classmethod
def post(cls, request, *args, **kwargs):
like_kind = request.data.get('like_kind')
include_code = request.data.get('include_code')
page_size = request.data.get('page_size')
pagination.PageNumberPagination.page_size = page_size
if include_code is None:
include_code = False
else:
if include_code == "False":
include_code = False
elif include_code == "True":
include_code = True
else:
return Response("Should be True или False for include_code",
status=status.HTTP_400_BAD_REQUEST)
if like_kind is None:
like_kind = "all"
else:
try:
likekind = LikeKind.objects.get(id=like_kind)
except LikeKind.DoesNotExist:
return Response("ID doesn't exists ",
status=status.HTTP_404_NOT_FOUND)
context = {"include_code": include_code, "like_kind": like_kind}
user_id = request.user.id # get the user_id from session
# TODO
# optionally other users with higher privilege can get access to other users' likes
# need to check for the privilege (admin, coordinator, ..)
# user_id = request.data.get('user_id')
if user_id is not None:
try:
user = User.objects.get(id=user_id)
users = [user, user, user, user, user]
serializer = LikeUserSerializer(users, many=True, context=context)
return Response(serializer.data)
except User.DoesNotExist:
return Response("ID doesn't exists ",
status=status.HTTP_404_NOT_FOUND)
return Response("user_id is not given",
status=status.HTTP_400_BAD_REQUEST)
My serializer.py:
class LikeUserSerializer(serializers.ModelSerializer):
likes = serializers.SerializerMethodField()
user_id = serializers.SerializerMethodField()
def get_user_id(self, obj):
return obj.id
def get_likes(self, obj):
include_code = self.context.get('include_code')
like_kind_id = self.context.get('like_kind')
likes = []
if like_kind_id == "all":
like_kinds = [(like_kind.id, like_kind.name, like_kind.get_icon_url()) for like_kind in LikeKind.objects.all()]
else:
like_kinds = [(like_kind.id, like_kind.name, like_kind.get_icon_url()) for like_kind in
[LikeKind.objects.get(id=like_kind_id)]]
# {"user_id": 1}
if include_code:
for like_kind in like_kinds:
items = []
transactions_liked = [(like.transaction_id, like.date_created)
for like in Like.objects.filter_by_user_and_like_kind(obj.id, like_kind[0])]
for transaction1 in transactions_liked:
items.append(
{
"transaction_id": transaction1[0],
"time_of": transaction1[1],
}
)
likes.append(
{
"like_kind": {
'id': like_kind[0],
'name': like_kind[1],
'icon': like_kind[2],
},
"items": items
}
)
return likes
else:
for like_kind in like_kinds:
items = []
transactions_liked = [(like.transaction_id, like.date_created)
for like in Like.objects.filter_by_user_and_like_kind(obj.id, like_kind[0])]
for transaction1 in transactions_liked:
items.append(
{
"transaction_id": transaction1[0],
"time_of": transaction1[1],
}
)
likes.append(
{
"like_kind": {
'id': like_kind[0],
},
"items": items
}
)
return likes
class Meta:
model = Like
fields = ['user_id', 'likes']
In urls.py I have the following path:
path('get-likes-by-user/', likes_views.LikesUserListAPIView.as_view()),
If you have any questions, please ask. Thanks, all!! I will appreciate your help!

Related

AttributeError at /update_item/ 'WSGIRequest' object has no attribute 'data'

i was trying to make my first e-commerce website using django and i received this error.
I already search in google but I can't find how to fix it. please help me to find the error.
this is attached. If any data is missing please comment.
cart.js :
var updateBtns = document.getElementsByClassName('update-cart')
for (var i = 0; i < updateBtns.length; i++) {
updateBtns[i].addEventListener('click', function () {
var productId = this.dataset.product`enter code here`
var action = this.dataset.action
console.log('productId:', productId, 'Action:', action)
console.log('USER:', user)
if (user == 'AnonymousUser') {
console.log('Not logged in')
} else {
updateUserOrder(productId)
}
})
}
function updateUserOrder(productId, action) {
console.log('User is logged in, sending data...')
var url = '/update_item/'
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken,
},
body: JSON.stringify({ 'productId': productId, 'action': action })
})
.then(response => response.json())
.then((data) => {
console.log('data:', data);
}
);
}
views.py :
from django.shortcuts import render
from django.http import JsonResponse
from django.http import HttpRequest
import json
from .models import *
def store(request):
products = Product.objects.all()
context = {'products': products}
return render(request, 'store/store.html', context)
def cart(request):
if request.user.is_authenticated:
customer = request.user.customer
order, created = Order.objects.get_or_create(
customer=customer, complete=False)
items = order.orderitem_set.all()
else:
# Create empty cart for now for non-logged in user
items = []
order = {'get_cart_total': 0, 'get_cart_items': 0}
context = {'items': items, 'order': order}
return render(request, 'store/cart.html', context)
def checkout(request):
if request.user.is_authenticated:
customer = request.user.customer
order, created = Order.objects.get_or_create(
customer=customer, complete=False)
items = order.orderitem_set.all()
else:
# Create empty cart for now for non-logged in user
items = []
order = {'get_cart_total': 0, 'get_cart_items': 0}
context = {'items': items, 'order': order}
return render(request, 'store/checkout.html', context)
def updateItem(request):
data = json.loads(request.data)
productId = data['productId']
action = data['action']
print('Action:', action)
print('productId:', productId)
return JsonResponse('Item was added', safe=False)'''
def updateItem(request):
data = json.loads(request.data)
request.data is available in Django rest framework, but it doesn't exist in regular Django views.
Change the view to use request.body instead.
def updateItem(request):
data = json.loads(request.body)

Queryset return response customize

I'm pretty new to Django restframework, what i'm trying now is to return object with foreignkey.
class User(models.Model):
name = models.CharField(max_length=255,blank=True)
date_created = models.DateTimeField(auto_now_add=True)
date_modiefied = models.DateTimeField(auto_now=True)
area = models.CharField(max_length=255,blank=True)
uuid = models.CharField(max_length=255)
home = models.CharField(max_length=255,blank=True)
work = models.CharField(max_length=255,blank=True)
mobileNo = models.CharField(max_length=255,blank=True)
email = models.CharField(max_length=255,blank=True)
appVersionCode = models.CharField(max_length=255,blank=True)
photoUrl = models.CharField(max_length=255,blank=True)
serverTime = models.CharField(max_length=255,blank=True)
fcmTokenId = models.CharField(max_length=255,blank=True)
def __str__(self):
return self.name
class LocationData(models.Model):
user = models.ForeignKey(
User, related_name='user', on_delete=models.DO_NOTHING)
source_id = models.CharField(max_length=255)
latitude = models.CharField(max_length=255)
longitude = models.CharField(max_length=255)
speed = models.CharField(max_length=255)
kms = models.CharField(max_length=255)
date_created = models.DateTimeField(auto_now=True)
date_modiefied = models.DateTimeField(auto
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
class LocationDataSerializer(serializers.ModelSerializer):
class Meta:
model = LocationData
fields = '__all__'
depth = 1
I'm using def get_queryset(self):
class SyncIndexLastDataViewSet(viewsets.ModelViewSet):
serializer_class = LocationDataSerializer
def get_queryset(self):
userid = self.request.query_params.get('user_id', None)
userExist = User.objects.filter(id=userid)
if userExist.exists():
# call the original 'list' to get the original response
queryset = LocationData.objects.values('source_id').filter(user__id=userid).order_by('-source_id')[:1]
lastSourceId = queryset[0]['source_id']
response = {"collection": {"data": lastSourceId,"statusCode": status.HTTP_200_OK,"version":"1.0"}}
json = JSONRenderer().render(response)
# customize the response data
if response is not None:
return json
else:
# return response with this custom representation
response = {"collection": {"data": "","statusCode":status.HTTP_404_NOT_FOUND,"version":"1.0","error":"Not found"}}
return response
Right now the result is inside the response is below and immediately it throws this error
But i want that queryset to return as below one, Hence i can read those key-pair values in android
{ "collection": {
"data": {
"id": 31,
"source_id": "55",
"latitude": "24654",
"longitude": "454654",
"date_created": "2019-02-08T17:10:09.318644Z",
"date_modiefied": "2019-02-08T17:10:09.318714Z",
"area": "54546",
"user": {
"id": 1,
"name": "Dormy",
"date_created": "1992-01-18T03:29:53.388000Z",
"date_modiefied": "2018-02-19T05:17:00.164000Z",
"serverTime": "",
"fcmTokenId": ""
}
},
"statusCode": 200,
"version": "1.0"
}
Now the error throws
AttributeError: Got AttributeError when attempting to get a value for field source_id on serializer LocationDataSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the int instance.
Original exception text was: 'int' object has no attribute 'source_id'.
Thanks!
The answer to this depends on what type of view you are using but the bottom line is you don't do this in get_queryset you do this in the method for the type of reguest.
For example if you are using a RetrieveAPIView you should override the retrieve method from the RetrieveModelMixin like so:
class MyAPIView(RetrieveAPIView):
queryset = MyModel.objects.all()
serializer_class = MySerializer
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
data = {
"collection": {
"data": serializer.data
},
"statusCode": 200,
"version": "1.0"
}
return Response(data)
If you are using something else like a ListAPIView then you want to see what is used by that in the relevant method and override that to wrap your data.
The main thing to realise here is that it has nothing to do with getting the queryset - which is just about getting data from the database. This is about transforming the data into the correct format when sending back a response. As a result the work should be done at the point the response is made.
There are couple of solution possible for this problem. NDevox already mention how we can overwrite our retrive function and get our expected response. But If we want this will be done with every response for every api end-point and if we go this way we need to overwrite every function then its quite burden and its DRY we should avoid this as possible. One of the possible way to introduce a middleware or overwrite Response so that we can get our generic response for every-api end-point without explicitly overwrite every functionality.
Possible Solution One
As we are using DRF here we can add our own return responses with various media types, say for application/json.
First We need to add in our settings.py
REST_FRAMEWORK = {
...
'DEFAULT_RENDERER_CLASSES': (
'app_name.renderers.ApiRenderer', # our own render middleware
),
...
}
And in our custom render middleware
from rest_framework.renderers import BaseRenderer
from rest_framework.utils import json
class ApiRenderer(BaseRenderer):
def render(self, data, accepted_media_type=None, renderer_context=None):
our_response_dict = {
'version': '1.0'
'data': {},
'message': '',
}
if data.get('data'):
our_response_dict['data'] = data.get('data')
if data.get('status'):
our_response_dict['statusCode'] = data.get('status')
if data.get('message'):
our_response_dict['message'] = data.get('message')
data = our_response_dict
return json.dumps(data)
Reference Link
Possible Solution Two
If we are using ModelViewset then there is another way we can achievement that. Say Our Views.py are like following
class A(serializer.ModelSerializer):
........
class B(serializer.ModelSerializer):
........
class C(serializer.ModelSerializer):
........
Our goal is to overwrite ModelViewset's to_representation function and return our custom result. This will like as following
from collections import OrderedDict
class OurParentViewset(serializer.ModelSerializer):
......
def to_representation(self, instance):
data = super(serializers.ModelSerializer, self).to_representation(instance)
result = OrderedDict()
result['data'] = data
result['version'] = '1.0'
result['statusCode'] = '2xx' # i am not fully sure how to customize this
return result
class A(OurParentViewset):
........
class B(OurParentViewset):
........
class C(OurParentViewset):
........
Implementing a custom renderer here seems to be a ways to go.
You can have requests from your android client include in the Accept header a way to identify the client to the renderer. 1 e.g.
Accept: application/json; android=true
Then compose a renderer using the JSONRenderer class to provide the format for your Android client.
# ./formatters/android_format.py
from rest_framework.renderers import JSONRenderer, BaseRenderer
from django.http.multipartparser import parse_header
class AndroidV1FormatRenderer(BaseRenderer):
media_type = 'application/json'
format = 'json'
json_renderer = JSONRenderer()
def android(self, accepted_media_type):
base_media_type, params = parse_header(accepted_media_type.encode('ascii'))
return 'android' in params
def render(self, data, accepted_media_type=None, renderer_context=None):
response = renderer_context['response']
android = self.android(accepted_media_type)
if android:
data = {
"collection": {"data": data},
"statusCode": response.status_code,
"version": "1.0"
}
return json_renderer.render(
wrapped_data, accepted_media_type, renderer_context)
This can then be used where you require response formatted that way using renderer_classes attribute of your APIView. 2
Since get_queryset won't allow you to customize the response data. I decide to take the query value that's important to me.
http://localhost/api/users/?user_id=1 --> changed into ...api/users/1
def retrieve(self, request, *args, **kwargs):
""" userid = self.request.query_params.get('user_id', None) """
userid = kwargs.get('pk')
userExist = User.objects.filter(id=userid)
if userExist.exists():
# call the original 'list' to get the original response
queryset = LocationData.objects.values('source_id').filter(user__id=userid).order_by('-source_id')[:1]
lastSourceId = queryset[0]['source_id']
response = {"collection": {"data": lastSourceId,"statusCode": status.HTTP_200_OK,"version":"1.0"}}
# customize the response data
if response is not None:
return Response(response)
else:
# return response with this custom representation
response = {"collection": {"data": "","statusCode":status.HTTP_404_NOT_FOUND,"version":"1.0","error":"Not found"}}
return response

ElasticSearch : Index a document in my django web application

I'm trying to use for the first time ElasticSearch in my web application and I have difficulties with my ES indexation.
The process is :
When I add a document with upload field, the document is indexed by ES
I have a function which let to define a new document title
I have to reindex this document with new title in order to appear in ES list of documents
I have some issues with this last part and I would like to know if you could help me.
Actual processus :
models.py file :
class Document(EdqmFullTable):
CAT_CHOICES = (...)
...
file = models.FileField(upload_to=upload_file)
def get_filename(self):
return os.path.join(settings.MEDIA_ROOT, str(self.file))
When a new document is added through this model, it calls ES methods :
es4omcl.py file :
class EdqmES(object):
host = 'localhost'
port = 9200
es = None
def __init__(self, *args, **kwargs):
self.host = kwargs.pop('host', self.host)
self.port = kwargs.pop('port', self.port)
# Connect to ElasticSearch server
self.es = Elasticsearch([{
'host': self.host,
'port': self.port
}])
def __str__(self):
return self.host + ':' + self.port
#staticmethod
def file_encode(filename):
with open(filename, "rb") as f:
return b64encode(f.read()).decode('utf-8')
def create_pipeline(self):
body = {
"description": "Extract attachment information",
"processors": [
{"attachment": {
"field": "data",
"target_field": "attachment",
"indexed_chars": -1
}},
{"remove": {"field": "data"}}
]
}
self.es.index(
index='_ingest',
doc_type='pipeline',
id='attachment',
body=body
)
def index_document(self, doc, bulk=False):
filename = doc.get_filename()
try:
data = self.file_encode(filename)
except IOError:
data = ''
print('ERROR with ' + filename)
# TODO: log error
item_body = {
'_id': doc.id,
'data': data,
'relative_path': str(doc.file),
'title': doc.title,
}
if bulk:
return item_body
result1 = self.es.index(
index='omcl', doc_type='annual-report',
id=doc.id,
pipeline='attachment',
body=item_body,
request_timeout=60
)
print(result1)
return result1
And signals from callback.py file when a new document is saved into the database :
#receiver(signals.post_save, sender=Document, dispatch_uid='add_new_doc')
def add_document_handler(sender, instance=None, created=False, **kwargs):
""" When a document is created index new annual report (only) with Elasticsearch and update conformity date if the
document is a new declaration of conformity
:param sender: Class which is concerned
:type sender: the model class
:param instance: Object which was just saved
:type instance: model instance
:param created: True for a creation, False for an update
:type created: boolean
:param kwargs: Additional parameter of the signal
:type kwargs: dict
"""
if not created:
return
# Update Conformity declaration date
if instance.category == Document.OPT_CD:
now = datetime.today()
Omcl.objects.filter(id=instance.omcl_id).update(last_conformity=now)
# Index only annual reports
elif instance.category == Document.OPT_ANNUAL:
es = EdqmES()
es.index_document(instance)
My process :
I defined a new class which let to handle document as soon as they are uploaded. I can modify the document title. So the last step is : reindex this modified document with ES.
class ManageDocView(AdminRequiredMixin, View, BaseException):
""" Render the Admin Manage documents to update year in the filename"""
template_name = 'omcl/manage_doc_form.html'
form_class = ManageDocForm
success_url = 'omcl/manage_doc_form.html'
def get(self, request):
form = self.form_class()
context = {
"form": form
}
return render(request, self.template_name, context)
def post(self, request):
form = self.form_class()
query_document_updated = None
query_omcl = None
query_document = None
if "SearchOMCL" in request.POST:
omcl_list = request.POST['omcl_list']
query_omcl = Omcl.objects.get(id=omcl_list)
query_document = Document.objects.filter(omcl=omcl_list)
elif "UpdateDocument" in request.POST:
checkbox_id = request.POST['DocumentChoice']
checkbox_id_minus_1 = int(checkbox_id) - 1
query_document_updated = Document.objects.get(id=checkbox_id)
print(query_document_updated.id)
omclcode = query_document_updated.omcl.code
src_filename = query_document_updated.src_filename
filename, file_extension = os.path.splitext(src_filename)
category = query_document_updated.category
if category == "ANNUAL":
category = "ANNUAL_REPORT"
year = self.request.POST.get('q1year')
# Create the new document title updated by the new year
new_document_title = f"{year}_{category}_{omclcode}_{checkbox_id_minus_1} - {src_filename}"
# Create the new document file updated by the new year
new_document_file = f"omcl_docs/{omclcode}/{year}_{category}_{omclcode}_{checkbox_id_minus_1}{file_extension}"
# Get file.name in order to rename document file in /media/
document_path = query_document_updated.file.name
try:
actual_document_path = os.path.join(settings.MEDIA_ROOT, document_path)
new_document_path_temp = settings.MEDIA_ROOT + "/" + new_document_file
new_document_path = os.rename(actual_document_path, new_document_path_temp)
except FileNotFoundError:
messages.error(self.request, _(f"Document {src_filename} doesn't exist in the server"))
return redirect('manage_doc')
else:
# Assign modifications to selected document and save it into the database
query_document_updated.title = new_document_title
query_document_updated.file = new_document_file
query_document_updated.save()
messages.success(self.request, _(f"The modification has been taken account"))
context = {
'form': form,
'query_omcl': query_omcl,
'query_document': query_document,
'query_document_updated': query_document_updated,
}
return render(request, self.template_name, context)
I'm completely list because I don't know How I can adapt this part in callback.py file :
if not created:
return
with my Django ManageDocView() class

Test POST method to Django REST viewsets

Docs says only mapping of GET
user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})
tests.py:
def test_admin_can_create_role(userprofiles, aoo_admin, bug_manager, note_admin):
aoo = User.objects.get(username='aoo')
factory = APIRequestFactory()
view = RoleViewSet.as_view()
url = reverse('api:role-list')
data = {
'name': 'FirstAdmin',
'type': Role.RoleType.admin,
'company': 1,
}
request = factory.post(url, data=data, format='json')
force_authenticate(request, user=aoo)
response = view(request)
assert 201 == response.data
viewsets.py
class RoleViewSetPermission(permissions.BasePermission):
message = 'Only Manager or Admin are allowed'
def has_permission(self, request, view):
user = request.user
return user.has_perm('roles.add_role') \
and user.has_perm('roles.change_role') \
and user.has_perm('roles.delete_role')
class RoleViewSet(viewsets.ModelViewSet):
permission_classes = (RoleViewSetPermission,)
queryset = Role.objects.all()
serializer_class = RoleSerializer
filter_backends = (filters.DjangoFilterBackend, SearchFilter)
filter_class = RoleFilter
search_fields = ('name', 'description', 'user__username', 'company__name', 'company__name_th')
def filter_queryset(self, queryset):
try:
company = self.request.user.user_profile.companyappid.company
except AttributeError:
logger.error(f'{self.request.user} has AttributeError')
return Role.objects.none()
else:
logger.info(f'{self.request.user} is {company} member')
return queryset.filter(company=company)
Trackback:
cls = <class 'poinkbackend.apps.roles.api.viewsets.RoleViewSet'>, actions = None, initkwargs = {}
#classonlymethod
def as_view(cls, actions=None, **initkwargs):
"""
Because of the way class based views create a closure around the
instantiated view, we need to totally reimplement `.as_view`,
and slightly modify the view function that is created and returned.
"""
# The suffix initkwarg is reserved for identifying the viewset type
# eg. 'List' or 'Instance'.
cls.suffix = None
# actions must not be empty
if not actions:
> raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`")
E TypeError: The `actions` argument must be provided when calling `.as_view()` on a ViewSet. For example `.as_view({'get': 'list'})`
../../.pyenv/versions/3.6.3/envs/poink/lib/python3.6/site-packages/rest_framework/viewsets.py:55: TypeError
Question:
How to do force_authenticate and request.post to the viewsets?
I have no problem with get. It has an answer already in the SO
References:
http://www.django-rest-framework.org/api-guide/viewsets/
I have to use APIClient not APIRequestFactory.
I though it has only one way to do testing.
Here is my example.
def test_admin_can_create_role(userprofiles, aoo_admin, bug_manager, note_admin):
aoo = User.objects.get(username='aoo')
client = APIClient()
client.force_authenticate(user=aoo)
url = reverse('api:role-list')
singh = Company.objects.get(name='Singh')
data = {
'name': 'HairCut',
'type': Role.RoleType.admin,
'company': singh.id, # Must be his companyid. Reason is in the RoleSerializer docstring
}
response = client.post(url, data, format='json')
assert 201 == response.status_code

Update the Queryset of a Django-Select2 AutoModelSelect2Field

I can't figure out how to update the queryset of a AutoModelSelect2Field dynamically. I'm getting really strange results. For example, sometimes the select2 box will return the correct, filtered results, and sometimes it will return NO results when I enter the same characters.
my code:
#views.py
form = MyForm()
#forms.py
class MyField(AutoModelSelect2Field):
search_fields = ['name__icontains']
max_results = 10
class MyForm(forms.Form):
my_field = MyField(
queryset=project.objects.none(),
required=True,
widget=AutoHeavySelect2Widget(
select2_options={
'width': '100%',
}
)
)
def __init__(self, *args, **kwargs):
qs = kwargs.pop('queryset')
self.base_fields['my_field'].queryset = qs
super(MyForm, self).__init__(*args, **kwargs)
#self.fields['my_field'].queryset = qs
#self.base_fields['my_field'].queryset = qs
A few of the things I've tried -
update from the view:
#views.py
form = MyForm()
form.base_fields['my_field'].queryset = new_qs
and:
form = MyForm()
form.fields['my_field'].queryset = new_qs
pass the qs to the form:
#views.py
form = MyForm(queryset=Project.objects.filter(project_type=pt))
# see above code for forms.py
I've also tried setting the initial qs to all objects:
class MyForm(forms.Form):
my_field = MyField(
queryset=project.objects,
...
But I get the same problem, 90% of the time I get the results of the initial queryset, rather than the filtered objects based on the new qs.
We were able to find a pretty straightforward way to get the dropdown options to filter by additional fields (ie. first select country and then have the state dropdown only showing states from the selected country)
It was inspired by a suggestion from here (where we also posted this solution):
https://github.com/applegrew/django-select2/issues/22
in forms.py:
class StateChoices(AutoModelSelect2Field):
queryset = State.objects
def get_results(self, request, term, page, context):
country = request.GET.get('country', '')
states = State.objects.filter(country=country, name__istartswith=term)
s2_results = [(s.id, s.name, {}) for s in states]
return ('nil', False, s2_results)
the form field:
# only include this when you would have a screen where the country
# is preset and would not change, but you want to use it as a search context
country = forms.ModelChoiceField(queryset=Country.objects.all(),
widget=forms.HiddenInput())
state = StateChoices(widget = AutoHeavySelect2Widget(
select2_options = {
'minimumInputLength': 1,
'ajax': {
'dataType': 'json',
'quietMillis': 100,
'data': JSFunctionInContext('s2_state_param_gen'),
'results': JSFunctionInContext('django_select2.process_results'),
}
}))
in our javascript:
function s2_state_param_gen(term, page) {
var proxFunc = $.proxy(django_select2.get_url_params, this);
results = proxFunc(term, page, 's2_condition_param_gen');
results.country = $('#id_country').val();
return results;
}

Categories

Resources