I am new to django and building a REST API using django-rest-framework
. I have written some code to check whether the user has supplied some parameters or not.But that is very ugly with lot of if conditions
, so i want to refactor it.Below is the code that i have written please suggest how to refactor it.
I am looking for some django based validations.
class AssetsViewSet(viewsets.ModelViewSet): queryset = Assets.objects.using("gpr").all() def create(self, request): assets = [] farming_details = {} bluenumberid = request.data.get('bluenumberid', None) if not bluenumberid: return Response({'error': 'BlueNumber is required.'}) actorid = request.data.get('actorid', None) if not actorid: return Response({'error': 'Actorid is required.'}) asset_details = request.data.get('asset_details', None) if not asset_details: return Response({'error': 'AssetDetails is required.'}) for asset_detail in asset_details: location = asset_detail.get('location', None) if not location: return Response({'error': 'location details is required.'}) assettype = asset_detail.get('type', None) if not assettype: return Response({'error': 'assettype is required.'}) asset_relationship = asset_detail.get('asset_relationship', None) if not asset_relationship: return Response({'error': 'asset_relationship is required.'}) subdivision_code = location.get('subdivision_code', None) if not subdivision_code: return Response({'error': 'subdivision_code is required.'}) country_code = location.get('country_code', None) if not country_code: return Response({'error': 'country_code is required.'}) locationtype = location.get('locationtype', None) if not locationtype: return Response({'error': 'locationtype is required.'}) latitude = location.get('latitude', None) if not latitude: return Response({'error': 'latitude is required.'}) longitude = location.get('longitude', None) if not longitude: return Response({'error': 'longitude is required.'}) try: country_instance = Countries.objects.using('gpr').get(countrycode=country_code) except: return Response({'error': 'Unable to find country with countrycode ' + str(country_code)}) try: subdivision_instance = NationalSubdivisions.objects.using('gpr').get(subdivisioncode=subdivision_code, countrycode=country_code) except: return Response({'error': 'Unable to find subdivision with countrycode ' + str(country_code) + ' and' + ' subdivisioncode ' + str(subdivision_code)}) kwargs = {} kwargs['pobox'] = location.get('pobox', '') kwargs['sublocation'] = location.get('sublocation', '') kwargs['streetaddressone'] = location.get('streetaddressone', '') kwargs['streetaddresstwo'] = location.get('streetaddresstwo', '') kwargs['streetaddressthree'] = location.get('streetaddressthree', '') kwargs['city'] = location.get('city', '') kwargs['postalcode'] = location.get('postalcode', '') cursor = connections['gpr'].cursor() cursor.execute("Select uuid() as uuid") u = cursor.fetchall() uuid = u[0][0].replace("-", "") kwargs['locationid'] = uuid # l.refresh_from_db() try: Locations.objects.using('gpr').create_location(locationtype=locationtype, latitude=latitude, longitude=longitude, countrycode=country_instance, subdivisioncode = subdivision_instance, **kwargs) except (TypeError, ValueError): return Response({'error': 'Error while saving location'}) try: location_entry = Locations.objects.using('gpr').get(locationid=uuid) except: return Response({'error': 'Unable to find location with locationid ' + str(uuid)}) asset_entry = Assets.objects.using('gpr').create(locationid=location_entry, assettype=assettype) asset_entry = Assets.objects.using('gpr').filter(locationid=location_entry, assettype=assettype).latest('assetinserted') farming_details[asset_entry.assetid] = [] try: actor = Actors.objects.using('gpr').get(actorid = actorid) except: return Response({'error': 'Unable to find actor with actorid ' + str(actorid)}) assetrelationship = AssetRelationships.objects.using('gpr').create(assetid= asset_entry, actorid= actor,assetrelationship=asset_relationship) assets.append(asset_entry) if assettype=="Farm or pasture land": hectares = asset_detail.get('hectares', None) if hectares is None: return Response({'error': 'hectares must be a decimal number'}) try: farmingasset = FarmingAssets.objects.using('gpr').create(assetid=asset_entry, hectares=hectares) except ValidationError: return Response({'error': 'hectares must be decimal value.'}) farmingasset = FarmingAssets.objects.using('gpr').filter(assetid=asset_entry, hectares=hectares).last() for type_detail in asset_detail.get('type_details', []): crop = type_detail.get('crop', '') hectare = type_detail.get('hectare', '') if crop != '' and hectare != '': try: h3code = ProductCodes.objects.using('gpr').get(h3code=crop) except: return Response({'error': 'Unable to find ProductCode with h3code' + str(crop)}) try: farming = Farming.objects.using('gpr').create(assetid=farmingasset, h3code=h3code, annualyield=hectare) farming_details[asset_entry.assetid].append(farming.farmingid) except Exception as e: return Response({'error': e}) else: return Response({'error': 'crop with hectare is required.'}) i = 0 data = {} for asset in assets: if farming_details[asset.assetid]: data[i] = {"assetid": asset.assetid, "assetbluenumber": asset.assetuniversalid, "farming_ids": farming_details[asset.assetid]} else: data[i] = {"assetid": asset.assetid, "assetbluenumber": asset.assetuniversalid} i+=1 return Response(data)
Asset Model
class Assets(models.Model): assetid = models.CharField(db_column='AssetID', primary_key=True, max_length=255) # Field name made lowercase. assetname = models.CharField(db_column='AssetName', max_length=255, blank=True, null=True) # Field name made lowercase. locationid = models.ForeignKey('Locations', models.DO_NOTHING, db_column='LocationID') # Field name made lowercase. assetuniversalid = models.CharField(db_column='AssetBluenumber', unique=True, blank=True, null=True, max_length=255) # Field name made lowercase. assettype = models.CharField(db_column='AssetType', max_length=45, blank=True, null=True) # Field name made lowercase. assetinserted = models.DateTimeField(db_column='AssetInserted', blank=True, null=True, auto_now_add=True) # Field name made lowercase. assetupdated = models.DateTimeField(db_column='AssetUpdated', blank=True, null=True, auto_now=True) # Field name made lowercase.
3 Answers
Answers 1
You can make serializers, they have a very easy way to validate your data. As in your case all the fields seem to be required it becomes even easier.
Create a file on you api app like:
serializers.py
#Import Serializers lib from rest_framework import serializers #Import your models here (You can put more than one serializer in one file) from assets.model import Assets #Now make you serializer class class AssetsSerializer(serializers.ModelSerializer): class Meta: model = Profile fields = '__all__' #This last line will put all the fields on you serializer #but you can also especify only some fields like: #fields = ('assetid', 'assetname')
On you view you can use your serializer(s) class to validate you data.
views.py
#Serializers from assets.serializers import AssetsSerializer #Libraries you can use from django.http import Http404 from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status class AssetsViewSet(viewsets.ModelViewSet): queryset = Assets.objects.using("gpr").all() def create(self, request): assets = [] farming_details = {} #Set your serializer serializer = AssetsSerializer(data=request.data) if serializer.is_valid(): #MAGIC HAPPENS HERE #... Here you do the routine you do when the data is valid #You can use the serializer as an object of you Assets Model #Save it serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
i took this all from the documentation. You can learn a lot doing the tutorial from the official site. I hope it helps.
Answers 2
You can do something like:
for param in ['bluenumberid', 'actorid', 'asset_details']: if param not in request.data.keys(): raise Response({'error': '%s is required.' % param}) ... for asset_detail in asset_details: for param in ['location', ..., 'longitude']: if param not in asset_detail.keys(): raise Response({'error': '%s is required.' % param})
Answers 3
This is just a guide that you can follow for refactoring, of course many other things can be improved while performing this:
- make a ModelSerializer for model Assets
- AssetsModelSerializer should handle validation
- within AssettsModelSerializer add any related ModelSerializer (like Locations) that has specific validation and representation
- move the create method to AssetsModelSerializer and just handle there the model creation
- AssetModelSerializer should provide a specific to_representation (if needed)
- The AssetsViewSet is doing more then one thing as I see (especially the last part with FarmingAssets objects) can you split that logic in another view? or route?
0 comments:
Post a Comment