I am using Django Rest Framework to perform get() post() on existing database. In the models, I haven't defined any primary or foreign key relations as these constraints are handled at database level. When I have debug = True in the settings.py file, the database level errors with traceback get displayed. However, when debug = False, I wish to display the errors in the response.
eg: error message:
IntegrityError at /dqf_api/ReceiptLog/
(1062, "Duplicate entry 'ede pivot-dummy-ede_case_76-ede pivot command 76' for key 'PRIMARY'")
When debug = True, this is displayed. When debug = False, how do I catch this error and display it?
EDIT : Including code modules
models.py
class TestCaseCommandRun(models.Model):
# fields ..Doesn't have id field as the database doesn't have it
class Meta:
managed = False
db_table = 'test_case_command_run'
unique_together = (('team_name', 'suite_name', 'suite_run_id', 'case_name', 'command_name'),)
class TestCaseCommandRunResults(models.Model):
# fields ..Doesn't have id field as the database doesn't have it
class Meta:
managed = False
db_table = 'test_case_command_run_results'
unique_together = (('suite_run_id', 'command_run_id', 'rule_name', 'result_id'),)
views.py
class TestCaseCommandRunViewSet(viewsets.ModelViewSet):
queryset = models.TestCaseCommandRunViewSet.objects.values('team_name','suite_name','suite_run_id', 'case_name','command_name','command_run_id','run_start','run_end','result','run_status')
serializer_class = serializers.TestCaseCommandRunViewSet
class TestCaseCommandRunResultsViewSet(viewsets.ModelViewSet):
queryset = models.TestCaseCommandRunResultsViewSet.objects.values('suite_run_id','command_run_id','rule_name', 'result_id',
'result','expected_values','actual_values','report_values','extended_values')
serializer_class = serializers.TestCaseCommandRunResultsViewSet
class ReceiptLogViewSet(CreateAPIView):
serializer_class = serializers.ReceiptLogSerializer.ReceiptLogSerializerClass
serializers.py
class TestCaseCommandRunResultsViewSet(serializers.ModelSerializer):
class Meta:
model = models.TestCaseCommandRunResultsViewSet
fields = ['suite_run_id','command_run_id','rule_name', 'result_id','result','expected_values','actual_values','report_values','extended_values']
class TestCaseCommandRunSerializer(serializers.ModelSerializer):
class Meta:
model = models.TestCaseCommandRunSerializer
fields = ['team_name','suite_name','suite_run_id', 'case_name','command_name','command_run_id','run_start','run_end','result','run_status']
class ReceiptLogSerializerClass(serializers.Serializer):
team_name = serializers.CharField(max_length=30)
suite_name = serializers.CharField(max_length=100)
suite_run_id = serializers.CharField(max_length=50,required=False, allow_blank=True, default=datetime.now().strftime('%Y%m%d%H%M%S'))
case_name = serializers.CharField(max_length=50)
command_name = serializers.CharField(max_length=50)
command_run_id = serializers.CharField(max_length=50,required=False, allow_blank=True, default='Not Applicable')
run_start = serializers.DateTimeField(default=datetime.now, required=False)
run_end = serializers.DateTimeField(default=datetime.now, required=False)
result = serializers.CharField(max_length=10, default='Not Applicable')
run_status = serializers.CharField(max_length=10)
rule_name = serializers.CharField( max_length=50, required=False, allow_blank=True, default='Not Applicable')
expected_values = serializers.CharField(max_length=200, allow_blank=True)
actual_values = serializers.CharField(max_length=200, allow_blank=True)
report_values = serializers.CharField(max_length=200, allow_blank=True)
extended_values = serializers.CharField(max_length=200, allow_blank=True)
def create(self, validated_data):
team_name = validated_data.get('team_name', None)
suite_name = validated_data.get('suite_name', None)
suite_run_id = validated_data.get('suite_run_id', None)
case_name = validated_data.get('case_name', None)
command_name = validated_data.get('command_name', None)
command_run_id = validated_data.get('command_run_id', None)
run_start = validated_data.get('run_start', None)
run_end = validated_data.get('run_end', None)
result = validated_data.get('result', None)
run_status = validated_data.get('run_status', None)
rule_name = validated_data.get('rule_name', None)
expected_values = validated_data.get('expected_values', None)
actual_values = validated_data.get('actual_values', None)
report_values = validated_data.get('report_values', None)
extended_values = validated_data.get('extended_values', None)
test_case_command_run_data = {
'team_name': team_name,
'suite_name': suite_name,
'suite_run_id': suite_run_id,
'case_name': case_name,
'command_name': command_name,
'command_run_id': command_run_id,
'run_start': run_start,
'run_end': run_end,
'result': result,
'run_status': run_status
}
TestCaseCommandRunSerializer.create(TestCaseCommandRunSerializer(), validated_data=test_case_command_run_data)
test_case_command_run_result_data = {
'suite_run_id': suite_run_id,
'command_run_id': command_run_id,
'rule_name': rule_name,
'result_id': self.result_id,
'result': result,
'expected_values': expected_values,
'actual_values': actual_values,
'report_values': report_values,
'extended_values': extended_values,
}
TestCaseCommandRunResultsSerializer.create(TestCaseCommandRunResultsSerializer(), validated_data=test_case_command_run_result_data)
self.result_id += 1
return validated_data
urls.py
router = routers.DefaultRouter()
router.register(r'test_case_command_runs', views.TestCaseCommandRunViewSet)
router.register(r'test_case_command_run_results', views.TestCaseCommandRunResultsViewSet)
urlpatterns = [
url(r'^dqf_api/', include(router.urls)),
url(r'^dqf_api/ReceiptLog/', views.ReceiptLogView.ReceiptLogViewSet.as_view(), name='ReceiptLog')]
EDIT: code Fix
Added this to serializers.py
def create(self, validated_data):
try:
return super().create(validated_data)
except IntegrityError as e:
error_msg = "IntegrityError occurred while creating entry in test_case_command_run_result model. Detailed Error: %s" %e
raise serializers.ValidationError(error_msg)
Maybe try something like this:
if serializer.is_valid():
serializer.save()
return Response("success")
else:
return Response(serialzier.errors)
Using a try catch statement
In views.py
class TestCaseCommandRunViewSet(viewsets.ModelViewSet):
try:
queryset = models.TestCaseCommandRunViewSet.objects.values('team_name','suite_name','suite_run_id', 'case_name','command_name','command_run_id','run_start','run_end','result','run_status')
serializer_class = serializers.TestCaseCommandRunViewSet
return Response("working")
except Exception as e:
return Response(e)
class TestCaseCommandRunResultsViewSet(viewsets.ModelViewSet):
try:
queryset = models.TestCaseCommandRunResultsViewSet.objects.values('suite_run_id','command_run_id','rule_name','result_id','result','expected_values','actual_values','report_values','extended_values')
serializer_class = serializers.TestCaseCommandRunResultsViewSet
retutn Response("Working")
except Exception as e:
return Response(e)
class ReceiptLogViewSet(CreateAPIView):
try:
serializer_class = serializers.ReceiptLogSerializer.ReceiptLogSerializerClass
return Response("working")
except Exception as e:
return Response(e)
Related
I'm having an issue in DRF tests when creating instance of a model, the status code in response is 'HTTP_201_CREATED' but the instance it self does not exist in the testing db.
here is my model :
class Item(SafeDeleteModel):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name=_("Owner"))
name = models.CharField(_("Name"), max_length=150)
category = TreeForeignKey('merssis.ItemCategory', on_delete=models.SET_NULL, null=True, verbose_name=_("Category"))
fixed_price = models.FloatField(_("Fixed price"), default=0)
main_pic = ProcessedImageField(verbose_name=_("Main picture"), upload_to='item_pics', processors=[ItemWatermarker()],format='JPEG')
main_pic_thumbnail = ImageSpecField(source='main_pic',
processors=[ResizeToFill(384, 256)],
format='JPEG',
options={'quality': 100})
geo_location = models.PointField(srid=4326, null=True, blank=True, verbose_name=_("Geolocation"))
_safedelete_policy = SOFT_DELETE_CASCADE
def __str__(self):
return self.name
Serializer :
class ItemCreateSerializer(GeoFeatureModelSerializer):
PRICE_TYPE_CHOICES = (
('fixed', _('Fixed') ),
('open', _('Open') ),
)
owner = serializers.HiddenField(default=serializers.CurrentUserDefault())
price_type = serializers.ChoiceField(choices=PRICE_TYPE_CHOICES)
category = serializers.PrimaryKeyRelatedField(queryset=ItemCategory.objects.all(), many=False)#ItemCategorySerializer(many=False)
main_pic = serializers.ImageField(use_url='item_pics')
def validate(self, data):
user = self.context['request'].user
geo_data = data.get('geo_location')
#Validate fixed price value
if data['price_type'] == 'fixed':
if data.get('fixed_price') == None or int(data.get('fixed_price')) <= 0:
raise serializers.ValidationError({"fixed_price" :INVALIDE_PRICE_ERROR})
#Price type is open should explicitly set fixed price to 0
if data['price_type'] == 'open':
data['fixed_price'] = 0
#Validate geo_location
#geo_location post data form ====> {"type":"Point", "coordinates":[37.0625,-95.677068]}
if geo_data:
if not validate_in_country_location(user, geo_data):
raise serializers.ValidationError({"geo_location":OUTSIDE_COUNTRY_MSG})
return data
def create(self, validated_data):
#Remove price_type value since it is not a field in the model
#We used to determine th price type on the serializer only
validated_data.pop('price_type')
return Item(**validated_data)
class Meta:
model = Item
geo_field = 'geo_location'
fields = ( 'owner',
'name',
'price_type',
'category',
'fixed_price',
'main_pic',
'geo_location',
)
The view :
class ItemCreateAPIView(CreateAPIView):
queryset = Item.objects.all()
serializer_class = ItemCreateSerializer
permission_classes = [permissions.IsAuthenticated]
def perform_create(self, serializer, *args, **kwargs):
self.check_object_permissions(self.request, self.request.user)
serializer.save()
The test case :
class ItemTestCase(APITestCase):
def test_create_new_item(self):
"""
Testing Add new item functionality
"""
self.client_1 = APIClient()
self.user_1 = create_new_user(email='tester1#gmail.com', username='tester_1', password='qsdf654654', gender='male')
self.client_1.login(username='tester_1',password='qsdf654654')
image_file = create_test_image()
category = ItemCategory.objects.create(name='SomeCat')
new_item_data = {
'name': 'New Item',
'price_type' : 'open',
'category': str(category.pk),
'main_pic': image_file,
}
response = self.client_1.post(url, new_item_data, format='multipart')
items = Item.objects.filter(name='New Item')
print(response.status_code)
self.assertEqual( response.status_code, status.HTTP_201_CREATED)
self.assertEqual( items.count(), 1)
and when i run the test i get '201' printed in console AND AssertionError: 0 != 1
i'm fvkin confused
In the serializer create() the object was never saved so change:
return Item(**validated_data)
to:
return Item.objects.create(**validated_data) # create the object
I have the following codes:
models.py
class Device(models.Model):
hostname = models.CharField(max_length=50, unique = True)
ipaddr = models.GenericIPAddressField(protocol='ipv4', unique=True, verbose_name='mangement IP') ##Use for mgt_id_addr
date_added = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.hostname
class DeviceDetail(models.Model):
SUBNET_CHOICES = (
('16','16'),
('17', '17'),
('18','18'),
('19','19'),
('20','20'),
('21', '21'),
('22', '22'),
('23', '23'),
('24', '24'),
('25', '25'),
('26', '26'),
('27', '27'),
('28', '28'),
('29', '29'),
('30', '30'),
)
DEV_MODS =(
('Catalyst 9606R', 'Catalyst 9606R'),
('C9300L-48T-4X', 'C9300L-48T-4X')
)
mgt_interface = models.CharField(max_length=50)
subnetmask = models.CharField(max_length=2, choices = SUBNET_CHOICES)
ssh_id = models.CharField(max_length=50)
ssh_pwd = models.CharField(max_length=50)
enable_secret = models.CharField(max_length=50)
dev_mod=models.CharField(max_length=50, choices = DEV_MODS) ##device_model replacement
DD2DKEY = models.ForeignKey(Device, on_delete=models.CASCADE) ##The key to link up the tables
def __str__(self):
return self.hostname
serializers.py
from rest_framework import serializers
from .models import Device, DeviceDetail
class DeviceSerializers(serializers.ModelSerializer):
class Meta:
model=Device
fields = '__all__'
class DeviceDetailSerializers(serializers.ModelSerializer):
class Meta:
model = DeviceDetail
fields = ['mgt_interface', 'subnetmask', 'ssh_id', 'ssh_pwd', 'enable_secret', 'dev_mod']
views.py
#api_view(['POST'])
def create_device(request):
device = Device()
devicedetail = DeviceDetail()
deviceserializer = DeviceSerializers(device, data = request.data)
devdserializer = DeviceDetailSerializers(devicedetail, data = request.data)
if deviceserializer.is_valid() and devdserializer.is_valid():
device_instance = deviceserializer.save()
devdserializer.save(DD2DKEY=device_instance)
results = {
"device":deviceserializer.data,
"device_details" : devdserializer.data,
}
return Response(results, status=status.HTTP_201_CREATED)
else:
errors = {
"device":deviceserializer.errors,
"device_details" : devdserializer.errors,
}
return Response(errors, status=status.HTTP_400_BAD_REQUEST)
I am facing the following error: AssertionError: You must call .is_valid() before accessing .errors.
How do i correct this to fixed the issue in case my codes enter the else condition so it will show the errors of the serializers? Because no matter what I still have to return a response if it failes
If deviceserializer.is_valid() fails, then the if condition will shortcircuit and thus not call devdserializer.is_valid(). You thus should ensure that it calls devdserializer.is_valid() as well:
if deviceserializer.is_valid() and devdserializer.is_valid():
device_instance = deviceserializer.save()
devdserializer.save(DD2DKEY=device_instance)
results = {
"device":deviceserializer.data,
"device_details" : devdserializer.data,
}
return Response(results, status=status.HTTP_201_CREATED)
else:
# ↓ ensure that is_valid() is called
devdserializer.is_valid()
errors = {
"device":deviceserializer.errors,
"device_details" : devdserializer.errors,
}
return Response(errors, status=status.HTTP_400_BAD_REQUEST)
I have made a filter api like this.
localhost/api/allpackages?price_min=700&price_max=900&destination=Spain&new_activity=Swimming&tour_type=Group%20Tour&featured=true&fix_departure=true
But according to new changes, I should be able to filter like this
localhost/api/allpackages?destination=Spain&destination=Japan&destination=Thailand....featured=true...
There can be multiple values for a single parameter, beacause user can now clik the chekboxes on the frontend. How can I achieve this?
My models:
class Package(models.Model):
operator = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
destination = models.ForeignKey(Destination, on_delete=models.CASCADE)
package_name = models.CharField(max_length=255)
featured = models.BooleanField(default=False)
price = models.IntegerField()
duration = models.IntegerField(default=5)
discount = models.CharField(max_length=255, default="15% OFF")
discounted_price = models.IntegerField(default=230)
savings = models.IntegerField(default=230)
tour_type = models.CharField(max_length=100, choices=TOUR_TYPE, default='Group Tour')
new_activity = models.ManyToManyField(NewActivity)
accommodation = models.CharField(max_length=255,default='Guest House & Hotel')
transport = models.CharField(max_length=150, default='Flight')
age_range = models.CharField(max_length=100, default='6 to 79 years old')
fix_departure = models.BooleanField(default=False)
....
...
My views:
class AllPackageAPIView(ListAPIView):
queryset = Package.objects.all()
serializer_class = PackageSerializer
filterset_class = PackageFilter
def get_queryset(self):
new_activity = self.request.query_params.get('new_activity', None)
destination = self.request.query_params.get('destination', None)
if new_activity is not None:
if destination is not None:
return Package.objects.filter(destination__name=destination, new_activity__title=new_activity)
else:
return Package.objects.filter(new_activity__title=new_activity)
elif destination is not None:
if new_activity is not None:
return Package.objects.filter(destination__name=destination, new_activity__title=new_activity)
else:
return Package.objects.filter(destination__name=destination)
else:
return Package.objects.all()
My filter:
class PackageFilter(filters.FilterSet):
price = filters.RangeFilter()
class Meta:
model = Package
fields = ['price','featured', 'fix_departure',
'tour_type',]
My serializers:
class PackageSerializer(serializers.ModelSerializer):
class Meta:
model = Package
fields = ['id', 'operator','destination', 'package_name', 'duration', 'featured', 'price', 'discount', 'discounted_price',
'tour_type','new_activity', 'accommodation', 'transport', 'age_range',
'savings', 'fix_departure', 'rating', 'image', 'date_created', ]
# fields = '__all__'
depth = 1
I have done this but now no data are shown. The get list is empty. I used get_queryset(self) as a function and now self.request.GET.get for querying.
MY updated view:
class AllPackageAPIView(ListAPIView):
queryset = Package.objects.all()
serializer_class = PackageSerializer
filterset_class = PackageFilter
def get_queryset(self):
new_activity = self.request.GET.get('new_activity', None)
destination = self.request.GET.get("destination", "")
destination_values = destination.split(",")
if new_activity is not None:
if destination is not None:
return Package.objects.filter(destination__name=destination_values, new_activity__title=new_activity)
else:
return Package.objects.filter(new_activity__title=new_activity)
elif destination is not None:
if new_activity is not None:
return Package.objects.filter(destination__name=destination_values, new_activity__title=new_activity)
else:
return Package.objects.filter(destination__name=destination_values)
else:
return Package.objects.all()
My solution:
def get_queryset(self):
# def get(self, request, format=None, *args, **kwargs):
new_activity = self.request.GET.get('new_activity',None)
destination = self.request.GET.get("destination",None)
tour_type = self.request.GET.get("tour_type",None)
if new_activity is not None:
new_activity = self.request.GET.get('new_activity', "")
new_activity_values = new_activity.split(",")
if destination is not None:
destination = self.request.GET.get("destination", "")
destination_values = destination.split(",")
if tour_type is not None:
tour_type = self.request.GET.get("tour_type", "")
tour_type_values = tour_type.split(",")
return Package.objects.filter(destination__name__in=destination_values,new_activity__title__in=new_activity_values,
tour_type__in=tour_type_values)
else:
return Package.objects.filter(destination__name__in=destination_values,
new_activity__title__in=new_activity_values)
else:
return Package.objects.filter(new_activity__title__in=new_activity_values)
elif destination is not None:
destination = self.request.GET.get("destination", "")
destination_values = destination.split(",")
if new_activity is not None:
new_activity = self.request.GET.get('new_activity', "")
new_activity_values = new_activity.split(",")
if tour_type is not None:
tour_type = self.request.GET.get("tour_type", "")
tour_type_values = tour_type.split(",")
return Package.objects.filter(destination__name__in=destination_values,
new_activity__title__in=new_activity_values,
tour_type__in=tour_type_values)
else:
return Package.objects.filter(destination__name__in=destination_values,
new_activity__title__in=new_activity_values
)
else:
return Package.objects.filter(destination__name__in=destination_values)
elif tour_type is not None:
tour_type = self.request.GET.get("tour_type", "")
tour_type_values = tour_type.split(",")
if destination is not None:
destination = self.request.GET.get("destination", "")
destination_values = destination.split(",")
if new_activity is not None:
new_activity = self.request.GET.get('new_activity', "")
new_activity_values = new_activity.split(",")
return Package.objects.filter(destination__name__in=destination_values,
new_activity__title__in=new_activity_values,
tour_type__in=tour_type_values)
else:
return Package.objects.filter(destination__name__in=destination_values,
tour_type__in=tour_type_values)
else:
return Package.objects.filter(tour_type__in=tour_type_values)
else:
return Package.objects.all()
This works as a filter for checkbox searches in ecommerce website. But it has a problem. When calling api, it repeats some of the objects ie same package object in my case. If anyone can solve it, let me know.
I have solved this question before, I've decided to get multiple values in URL by using split , character.
Example: URL: localhost/api/allpackages?destination=Spain,Japan,Thailand....featured=true...
destination = self.request.GET.get("destination", "")
destination_values = destination.split(",")
Sample code about filtering first_name, last_name, and multiple values of username in User model.
model.py
class User(AbstractUser):
#property
def full_name(self):
"""Custom full name method as a property"""
return str(self.first_name) + ' ' + str(self.last_name)
def __str__(self):
return self.email
view.py
class UserFilter(filters.FilterSet):
class Meta:
model = User
fields = ['first_name', 'last_name']
class ListCreateUser(ListCreateAPIView):
"""
List and Create User Generic contains create and list user APIs.
"""
serializer_class = UserSerializer
queryset = User.objects.all()
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = UserFilter
def get_queryset(self):
username = self.request.GET.get('username', '')
if username:
username_values = username.split(',')
return User.objects.filter(username__in=username_values)
return User.objects.all()
Results:
Filter by username
I found that Django supports multi-value parameters with its QueryDict since at least 3.0 . So when you have the situation like:
https://www.example.com/foo?a=1&a=2
you can get all values of a with:
def my_function(request):
a_list = request.query_params.getlist('a')
# [1, 2]
This is not intuitive, since request.query_params.get('a') only returns the last element in the list (see documentation).
django-rest-framework does not provide multi-value filter support, you have to write it yourself if you want OR you can use djangorestframework-jsonapi it provides the multi-value filter and many other pluggable features
Membership in a list of values: ?filter[name.in]=abc,123,zzz (name in ['abc','123','zzz'])
You can configure the filter backends either by setting the REST_FRAMEWORK['DEFAULT_FILTER_BACKENDS'] or individually add them as .filter_backends
'DEFAULT_FILTER_BACKENDS': (
'rest_framework_json_api.filters.QueryParameterValidationFilter',
'rest_framework_json_api.filters.OrderingFilter',
'rest_framework_json_api.django_filters.DjangoFilterBackend',
'rest_framework.filters.SearchFilter',
),
See this example
https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#configuring-filter-backends
Your code with changes you don't need to write PackageFilter and get_queryset
from rest_framework_json_api import django_filters
class AllPackageAPIView(ListAPIView):
queryset = Package.objects.all()
serializer_class = PackageSerializer
filter_backends = (django_filters.DjangoFilterBackend)
filterset_fields = {'destination': ('exact', 'in'), ...}
This is my model file which has the owner field name
class PotholeImages(models.Model):
"""Upload images with details """
image = models.ImageField(
upload_to='photos/%Y/%m/%d',
)
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
default = 'unknown',
)
state_name = models.CharField(
blank = False,
max_length = 30,
default = 'unknown',
)
country_name = models.CharField(
blank = False,
max_length = 30,
default = 'unknown',
)
name = models.CharField(default=uuid.uuid4, max_length=40)
cordinate_X = models.DecimalField(max_digits=22, decimal_places=16)
cordinate_Y = models.DecimalField(max_digits=22, decimal_places=16)
created_date = models.DateTimeField(auto_now_add=True)
road_name = models.CharField(max_length = 100, default = 'unknown')
def __str__(self):
return self.name
Serializer file
class ImageSerializer(serializers.ModelSerializer):
"""Uploading Pothoele Images"""
class Meta:
model = PotholeImages
fields = ('image','state_name',
'country_name','cordinate_X',
'cordinate_Y','road_name',)
def to_representation(self, instance):
rep = super(ImageSerializer, self).to_representation(instance)
rep['owner'] = instance.owner.username
# print(rep)
return rep
views file
class ImageViewSet(viewsets.ModelViewSet):
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
queryset = PotholeImages.objects.all()
serializer_class = serializers.ImageSerializer
def perform_create(self, serializer):
print(self.request.user)
serializer.save(user=self.request.user)
I want whenever a person sends a post request on
http://127.0.0.1:8000/images_api/imageView/
Then from the token, username gets detected and it should be passed in the owner field name.
Could anyone please help
Thanks
Finally, I have found the answer I have made through APIView
class ImageAPIView(APIView):
authentication_classes = (TokenAuthentication,)
def post(self, request):
## username
user = request.user
is_tokened = Token.objects.filter(user = user)
print("user name is ", user)
data = request.data
data['owner'] = user.id
serializer = serializers.ImageSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
ResponseData = "{} image sent by user".format(user.id)
return Response(ResponseData, status=status.HTTP_200_OK)
I have problem with rest_framework.viewsets.ReadOnlyModelViewSet.
class ProductFilter(filters.FilterSet):
meat_type = filters.CharFilter(lookup_expr='slug__iexact')
category = filters.CharFilter(lookup_expr='slug__iexact')
class Meta:
model = Product
fields = {
'price': ['gte', 'lte'],
}
ordering_fields = ['price', ]
class ProductViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
filterset_class = ProductFilter
#action(methods=['get'], detail=False)
def get_products(self, request):
products = self.get_queryset().order_by('-created')
serializer = self.get_serializer_class()(products, many=True)
print('SHOW IT')
if len(products) == 0:
return Response(status=status.HTTP_204_NO_CONTENT)
return Response(serializer.data, status=status.HTTP_200_OK)
My problem is that print in get_products not work, but code give good result with filters objects. My urls:
router = routers.DefaultRouter()
router.register('', views.ProductViewSet)
urlpatterns = [
path('shop/', include(router.urls))
]
Tests:
class TestViews(TestCase):
def setUp(self):
self.client = Client()
self.url = "/api/shop/"
self.search_url = "/api/shop/?price__lte={}&price__gte={}&meat_type={}&category={}"
self.category1 = Category.objects.create(name='cattest1',
slug='cattest1')
self.category2 = Category.objects.create(name='cattest2',
slug='cattest2')
self.meat_type1 = MeatType.objects.create(name='meattest1',
slug='meattest1')
self.meat_type2 = MeatType.objects.create(name='meattest2',
slug='meattest2')
self.product1 = Product.objects.create(category=self.category1,
meat_type=self.meat_type2,
name='prodtest1',
slug='prodtest1',
price=50)
self.product2 = Product.objects.create(category=self.category1,
meat_type=self.meat_type1,
name='prodtest2',
slug='prodtest2',
price=75)
self.product3 = Product.objects.create(category=self.category2,
meat_type=self.meat_type2,
name='prodtest3',
slug='prodtest3',
price=20)
self.product4 = Product.objects.create(category=self.category2,
meat_type=self.meat_type1,
name='prodtest4',
slug='prodtest4',
price=150)
def test_get_products_all(self):
response = self.client.get(self.url)
self.assertEqual(200, response.status_code)
self.assertEqual(4, len(response.data))
def test_get_products_no_content(self):
Product.objects.all().delete()
response = self.client.get(self.url)
self.assertEqual(204, response.status_code)
def test_product_greater_than(self):
response = self.client.get(self.search_url.format(
"", "55", "", ""
))
self.assertEqual(200, response.status_code)
self.assertEqual(2, len(response.data))
Test test_get_products_no_content fail with error:
assertionError: 204 != 200.
Somebody have any idea?
Thanks for any answer
Magnus
EDIT
Created this function, is pass good data to filter.
DICT {'price__lte': '50', 'price__gte': '100', 'meat_type': 'wieprzowina'}
But I have problem when I put it as filter argument. Got error:
invalid literal for int() with base 10: 'wieprzowina'. It try to change string to number, but I dont know why.
def get_queryset(self):
filter_params = self.request.query_params
filter_params_dict = {k: str(v) for (k, v) in filter_params.dict().items()
if v is not None and str(v) != ""}
print('DICT', filter_params_dict)
queryset = Product.objects.filter(**filter_params_dict)
return queryset
EDIT 2:
class Product(models.Model):
category = models.ForeignKey(Category,
related_name='products',
on_delete=models.CASCADE)
meat_type = models.ForeignKey(MeatType,
on_delete=models.CASCADE)
name = models.CharField(max_length=150,
db_index=True)
slug = models.SlugField(max_length=150,
db_index=True)
image = models.ImageField(upload_to='products/%Y/%m/%d',
default='no-image.png')
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
available = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
ordering = ('price',)
index_together = (('id', 'slug'),)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('shop:detail',
args=[self.category.slug, self.id, self.slug])
You are using the incorrect url ,you should use this url for your custom action:
/api/shop/get_products/
The url you are using i.e /api/shop/ will call the default list action of the viewset not your custom action and thats why you are getting 200 status code always
You can read more about viewset here:
ViewSets