I am trying to develop a REST API with django-rest-framework for updating a django model.
I want to unit test it with the following unit test
from rest_framework.test import APITestCase
class PatchInvestmentTest(APITestCase):
def test_repartition(self):
investment = Investment.objects.create()
sponsor1 = Investment.objects.create(InvestmentSponsor, name='A')
sponsor2 = Investment.objects.create(InvestmentSponsor, name='B')
url = reverse('investments:investments-detail', args=[investment.id])
data = {
'sponsorships': [
{'sponsor': sponsor1.id, 'percentage': 80},
{'sponsor': sponsor2.id, 'percentage': 10},
]
}
print("> data", data)
response = self.client.patch(url, data=data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(1, Investment.objects.count())
investment = Investment.objects.all()[0]
# It fails below : no investments are created
self.assertEqual(len(investment.sponsorships()), 2)
The model can be summed up with
class Investment(models.Model):
# ... a few fields
def sponsorships(self):
return self.investmentsponsorship_set.all().order_by('sponsor__ordering', 'sponsor__name')
class InvestmentSponsor(models.Model):
name = models.CharField(max_length=200, verbose_name=_('name'))
ordering = models.IntegerField(default=0)
class Meta:
ordering = ('ordering', 'name', )
class InvestmentSponsorship(models.Model):
sponsor = models.ForeignKey(InvestmentSponsor)
investment = models.ForeignKey(Investment)
percentage = models.DecimalField(max_digits=5, decimal_places=2)
The api is using rest-framework base classes
class InvestmentViewSet(viewsets.ModelViewSet):
model = Investment
def get_serializer_class(self):
serializers_class_map = {
'default': InvestmentSerializer,
'partial_update': PartialUpdateInvestmentSerializer,
}
return serializers_class_map.get(self.action, serializers_class_map['default'])
def perform_update(self, serializer):
serializer.save()
Then I expect to get and handle the "sponsorhips" data in the serializers
class InvestmentSponsorshipSerializer(serializers.ModelSerializer):
class Meta:
model = models.InvestmentSponsorship
fields = ('sponsor', 'percentage', )
class PartialUpdateInvestmentSerializer(serializers.ModelSerializer):
sponsorships = InvestmentSponsorshipSerializer(many=True)
class Meta:
model = models.Investment
fields = (
'id', '... others', 'sponsorships',
)
def validate_sponsorships(self, value):
print("validate_sponsorships", value)
return value
def update(self, instance, validated_data):
"""update only fields in data"""
data = validated_data.copy()
print("*** DATA", validated_data)
instance.save()
return instance
The problem is that the data I received from the serializer is empty
> data {'sponsorships': [{'sponsor': 1, 'percentage': 80}, {'sponsor': 2, 'percentage': 10}]}
validate_sponsorships []
*** DATA {'sponsorships': []}
This seems to occur only when unit testing. It seems to work from the dango-rest-framework admin.
I've tried to investigate why I don't received the data as validated_data in the update with no success yet.
Any idea?
You should add format parameter when calling patch:
response = self.client.patch(url, data=data, format='json')
Default multipart format does not support nesting I think.
I found a solution by using regular django unit-test class. A bit more difficult but it works
from django.test import TestCase
class PatchInvestmentTest(TestCase):
def test_repartition(self):
investment = Investment.objects.create()
sponsor1 = mommy.make(InvestmentSponsor, name='A', ordering=3)
sponsor2 = mommy.make(InvestmentSponsor, name='B', ordering=2)
url = reverse('investments:investments-detail', args=[investment.id])
data = {
"sponsorships": [
{"sponsor": sponsor2.id, "percentage": 80},
{"sponsor": sponsor1.id, "percentage": 10},
]
}
date_as_json = json.dumps(data)
response = self.client.patch(url, data=date_as_json, content_type="application/json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(1, Investment.objects.count())
investment = Investment.objects.all()[0]
self.assertEqual(len(investment.sponsorships()), 2)
Related
I am facing an issue while getting the promary_ket(id) of record ListApiView in the Django rest framework. I am also implementing the Custom pagination.
Here is my serializer:
class AllTestListSerializer(serializers.ModelSerializer):
strip_img = serializers.FileField(required=False)
cropped_img = serializers.FileField(required=False)
class Meta:
model = TakeTestDetails
fields = ['strip_img',
'cropped_img',
'period_cd',
'date',
'time',
'test_type',
'test_result',
'cycle_id',
'raw_data']
Here is my ListApiView Code:
class TestList(ListAPIView):
permission_classes = (IsAuthenticated, )
serializer_class = AllTestListSerializer
pagination_class = CustomPagination
def get_queryset(self):
return TakeTestDetails.objects.filter(user=self.request.user).order_by('date', 'time').all()
And here is my CustomPagination code:
class CustomPagination(pagination.PageNumberPagination):
def get_paginated_response(self, data):
response_data = {
'status': '200',
'message': 'Success',
'count': self.page.paginator.count,
'next': self.get_next_link(),
'previous': self.get_previous_link(),
}
if data:
new_cycle_data = []
for row in data:
row_dict = dict(row)
print(row)
cycle_id = row_dict['cycle_id']
cycle = TestCycle.objects.get(id=cycle_id)
cycle_no = cycle.cycle_no
cycle_start_date = cycle.cycle_start_date
cycle_end_date = cycle.cycle_end_date
cycle_start_date = change_date_format(cycle_start_date)
cycle_end_date = change_date_format(cycle_end_date)
test_date = change_date_format(row_dict['date'])
row_dict['date'] = test_date
row_dict['cycle_no'] = cycle_no
row_dict['cycle_start_date'] = cycle_start_date
row_dict['cycle_end_date'] = cycle_end_date
new_cycle_data.append(row_dict)
response_data['data'] = new_cycle_data
else:
return Response({
'status': '404',
'message': "You haven't performed any test yet",
'count': self.page.paginator.count,
'next': self.get_next_link(),
'previous': self.get_previous_link(),
'data': data
})
return Response(response_data)
Now what i want is also to get the primary_key/id of each row/record. But it's not giving me the id in data. How can i get the id?
Thanks in advance
add ID to the serializer
class AllTestListSerializer(serializers.ModelSerializer):
strip_img = serializers.FileField(required=False)
cropped_img = serializers.FileField(required=False)
class Meta:
model = TakeTestDetails
fields = ['id'
'strip_img',
'cropped_img',
'period_cd',
'date',
'time',
'test_type',
'test_result',
'cycle_id',
'raw_data']
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
I am following this tutorial for using Graphene with Django, and everything was going smooth, until I reached the Integration with Django Rest Framework section.
This section says that you can reuse DRF serializers with Graphene, by creating serializers clones, but it doesn't say what to do with such clones in order to reuse DRF serializers with Graphene.
These are my serializers and clones:
from rest_framework import serializers
from graphene_django.rest_framework.mutation import SerializerMutation
from GeneralApp.models import Airport
from ReservationsManagerApp.serializers import ReservationSerializer
from ReservationsManagerApp.models import ReservationComponent, ReservationHotel, ReservationRoundtrip, ReservationTransfer, ReservationTour, ReservationService, Hotel
class ReservationMutation(SerializerMutation):
class Meta:
serializer_class = ReservationSerializer
class ReservationComponentGraphSerializer(serializers.ModelSerializer):
component = serializers.SerializerMethodField()
class Meta:
model = ReservationComponent
fields = ('id', 'reservation', 'dertour_bk', 'day', 'content_type', 'object_id', 'comment', 'is_invoiced', 'component')
def get_component(self, instance):
components_models = {
'reservationhotel': ReservationHotel,
'reservationroundtrip': ReservationRoundtrip,
'reservationtransfer': ReservationTransfer,
'reservationtour': ReservationTour,
'reservationservice': ReservationService,
}
component = components_models[instance.content_type.model].objects.get(id=instance.object_id)
return self.get_component_string(instance.content_type.model, component)
def get_component_string(self, component_model, component):
components_get_string = {
'reservationhotel': self.get_hotel_string,
'reservationroundtrip': self.get_roundtrip_string,
'reservationtransfer': self.get_transfer_string,
'reservationtour': self.get_tour_string,
'reservationservice': self.get_service_string,
}
return components_get_string[component_model](component):
def get_hotel_string(self, component):
return component.hotel.name
def get_roundtrip_string(self, component):
return component.roundtrip.name
def get_transfer_string(self, component):
origin_str = self.get_place_str('origin', component)
destination_str = self.get_place_str('destination', component)
return "{} => {}".format(origin_str, destination_str)
def get_place_str(self, case, component):
places_models = {
'airport': Airport,
'hotel': Hotel,
}
if case == 'origin':
return places_models[component.origin_content_type.model].objects.get(id=component.origin_object_id).name
else:
return places_models[component.destination_content_type.model].objects.get(id=component.destination_object_id).name
def get_tour_string(self, component):
return component.tour.name
def get_service_string(self, component):
return component.service.name
class ReservationComponentMutation(SerializerMutation):
class Meta:
serializer_class = ReservationComponentGraphSerializer
And this is my schemas.py:
import graphene
from graphene_django.types import DjangoObjectType
from ReservationsManagerApp.models import Reservation, ReservationComponent
from InvoicesManagerApp.models import Invoice, InvoiceEntry, InvoiceEntryComponent
from PaymentsManagerApp.models import Payment, PaymentReservationComponent
class ReservationType(DjangoObjectType):
class Meta:
model = Reservation
class ReservationComponentType(DjangoObjectType):
class Meta:
model = ReservationComponent
class InvoiceType(DjangoObjectType):
class Meta:
model = Invoice
class InvoiceEntryType(DjangoObjectType):
class Meta:
model = InvoiceEntry
class InvoiceEntryComponentType(DjangoObjectType):
class Meta:
model = InvoiceEntryComponent
class PaymentType(DjangoObjectType):
class Meta:
model = Payment
class PaymentReservationComponentType(DjangoObjectType):
class Meta:
model = PaymentReservationComponent
class Query(object):
all_reservations = graphene.List(ReservationType)
all_reservation_components = graphene.List(ReservationComponentType)
all_invoices = graphene.List(InvoiceType)
all_invoice_components = graphene.List(InvoiceEntryType)
all_invoice_entries_components = graphene.List(InvoiceEntryComponentType)
all_payment = graphene.List(PaymentType)
all_payment_reservation_components = graphene.List(PaymentReservationComponentType)
def resolve_all_reservations(self, info, **kwargs):
return Reservation.objects.all()
def resolve_all_reservation_components(self, info, **kwargs):
return ReservationComponent.objects.select_related('reservation').all()
def resolve_all_invoice_entries_components(self, info, **kwargs):
return InvoiceEntryComponent.objects.select_related('reservation_component').all()
def resolve_all_payment_reservation_components(self, info, **kwargs):
return PaymentReservationComponent.objects.select_related('reservation_component').all()
I don't know if I am missing something obvious, but I can't understand how am I suppose to use those serializers mutations with graphene. I guess it must be by configuring the Query class in some way, but I can't find a reference in the documentation.
I don't see any reason why we have to do as shown in that tutorial. It is much easier to connect drf and graphql in following way. Doing this way,you do not need to worry about any vague classes and just rely on main aspects of drf and graphene.
Construct drf serializers normally, and connect it to graphql as shown below.
Consider we have model Subject. Let's create CRUD api for it.
from graphene.types.scalars import Scalar
class ObjectField(Scalar): # to serialize error message from serializer
#staticmethod
def serialize(dt):
return dt
class SubjectType(DjangoObjectType):
class Meta:
model=Subject
# For mutation, use serializers
#creating subject
class CreateSubject(graphene.Mutation):
subject=graphene.Field(SubjectType)
message=ObjectField()
status=graphene.Int()
class Arguments:
name=graphene.String(required=True)
description=graphene.String(required=True)
#classmethod
def mutate(cls,root,info,**kwargs):
serializer=SubjectSerializer(data=kwargs)
if serializer.is_valid():
obj=serializer.save()
msg='success'
else:
msg=serializer.errors
obj=None
print(msg)
return cls(subject=obj,message=msg,status=200)
'''Updating subject'''
class UpdateSubject(graphene.Mutation):
subject=graphene.Field(SubjectType)
status=graphene.Int()
message=ObjectField()
class Arguments:
id=graphene.ID(required=True)
name=graphene.String()
description=graphene.String()
#classmethod
def mutate(cls,root,info,id,**kwargs):
sub=Subject.objects.get(id=id)
serializer=SubjectSerializer(sub,data=kwargs,partial=True)
if serializer.is_valid():
obj=serializer.save()
msg='success'
else:
msg=serializer.errors
obj=None
print(msg)
return cls(subject=obj,message=msg,status=200)
'''Delete Subject'''
class DeleteSubject(graphene.Mutation):
message=ObjectField()
status=graphene.Int()
class Arguments:
id=graphene.ID(required=True)
#classmethod
def mutate(cls,root,info,id,**kwargs):
c=Subject.objects.get(id=id)
c.delete()
return cls(message='success',status=200)
class Mutation(graphene.ObjectType):
create_subject=CreateSubject.Field()
update_subject=UpdateSubject.Field()
delete_subject=DeleteSubject.Field()
# Query is normal.
class Query(graphene.ObjectType):
subject=graphene.Field(SubjectType,id=graphene.Int(), slug=graphene.String())
subjects=graphene.List(SubjectType)
def resolve_subject(self, info, id=None, slug=None):
if id:
return Subject.objects.get(id=id)
if slug:
return Subject.objects.get(slug=slug)
def resolve_subjects(self,info,**kwargs):
return Subject.objects.all()
You can try making little framework-like thing for yourself to avoid redundant code as seen.
From your example:
import graphene
from your.schemas import Query as YourQuery
from your.serializers import ReservationComponentMutation
# notice its an objecttype, and i've also added some debug
class Mutation(graphene.ObjectType):
debug = graphene.Field(DjangoDebug, name="_debug")
create_reservation = ReservationComponentMutation.Field()
class Query(YourQuery, graphene.ObjectType):
pass
class Mutation(Mutation, graphene.ObjectType):
pass
root_schema = graphene.Schema(query=Query, mutation=Mutation)
And the url:
urlpatterns = (
url(r"^graphql", GraphQLView.as_view(schema=root_schema graphiql=True), name="graphql"),
)
# you can wrap csrf_exempt(GraphQLView.as_view(...)) for testing
# or you can setup the frontend `apollo-client` to use csrf_tokens
# also see the related links below
For example for apollo-client and not apollo-boost the window.csrf_token is {% csrf_token %} in the template rendering:
import { ApolloProvider } from "react-apollo";
import ApolloClient from "apollo-client";
import { HttpLink } from "apollo-link-http";
import { InMemoryCache as Cache } from "apollo-cache-inmemory";
import { ApolloLink } from "apollo-link";
import fetch from "unfetch";
const uri = UrlUtils.makeUrl(Urls.graphQl);
const AuthLink = (operation, next) => {
const token = window.csrf_token;
operation.setContext(context => ({
...context,
headers: {
...context.headers,
"X-CSRFToken": token
}
}));
return next(operation);
};
const link = ApolloLink.from([
AuthLink,
new HttpLink({
uri,
credentials: "same-origin",
fetch // override fetch implementation for polyfills
})
]);
const apollo = new ApolloClient({
link,
cache: new Cache().restore({})
});
You should then be able to:
query TestQuery() {
resolveAllReservations () {
id
}
}
or:
mutate TestMutate($input: ReservationComponentMutationInput!) {
createReservation(input: $input) {
id
errors {
field
messages
}
}
_debug {
sql {
rawSql
}
}
}
related:
https://stackoverflow.com/a/39026832/2026508
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
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;
}