简体   繁体   English

在viewsets.ModelViewSet上获取params验证

[英]Get params validation on viewsets.ModelViewSet

I am new to django and building a REST API using django-rest-framework . 我是django的新手,并使用django-rest-framework构建REST API。 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. 我已经编写了一些代码来检查用户是否提供了一些参数。但是if conditions很多, if conditions非常难看了,所以我想重构它.Below是我编写的代码,请建议如何重构它。

I am looking for some django based validations. 我正在寻找一些基于django的验证。

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.

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: 在你的api app上创建一个文件,如:

serializers.py 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 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. 我希望它有所帮助。

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}) 

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 为模型资产制作ModelSerializer
  • AssetsModelSerializer should handle validation AssetsModelSerializer应该处理验证
  • within AssettsModelSerializer add any related ModelSerializer (like Locations) that has specific validation and representation 在AssettsModelSerializer中添加任何具有特定验证和表示的相关ModelSerializer(如Locations)
  • move the create method to AssetsModelSerializer and just handle there the model creation 将create方法移动到AssetsModelSerializer并只处理模型创建
  • AssetModelSerializer should provide a specific to_representation (if needed) AssetModelSerializer应提供特定的to_representation(如果需要)
  • 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? 正如我所看到的那样,AssetsViewSet做了一件事(尤其是FarmingAssets对象的最后一部分)你可以在另一个视图中拆分该逻辑吗? or route? 还是路线?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM