10

Following is my model definition

class ProfessionalQualification(Log_Active_Owned_Model_Mixin):

    PROF_TEACHER    = 1
    PROF_ENGINEER   = 2
    PROF_DOCTOR     = 4
    PROF_PROFESSOR  = 8
    PROF_MANAGER    = 16
    PROF_CLERK      = 32
    PROF_SALESMAN   = 64
    PROF_BUSINESSMAN= 128
    PROF_OTHER      = 129

    VALID_PROFESSIONS = (
        (PROF_TEACHER,      "Teacher"   ),
        (PROF_ENGINEER,     "Engineer"  ),
        (PROF_DOCTOR,       "Doctor"    ),
        (PROF_PROFESSOR,    "Professor" ),
        (PROF_MANAGER,      "Manager"   ),
        (PROF_CLERK,        "Clerk"     ),
        (PROF_SALESMAN,     "Salesman"  ),
        (PROF_BUSINESSMAN,  "Businessman"),
        (PROF_OTHER,        "Other"     )
    )

    profession_type         = IntegerField(choices=VALID_PROFESSIONS, null=False, blank=False)
    profession_type_name    = CharField(max_length=60, null=True, blank=True)
    institue                = CharField(max_length=160, null=False, blank=False)
    address                 = ForeignKey(to=City, null=False)
    year_start              = CurrentYearField(null=False, blank=False)
    in_progress             = BooleanField(null=False, blank=False)
    year_end                = CurrentYearField(null=True, blank=True)

Following is my serializer

class ProfQualSerializer(OwedModelSerializerMixin, ModelSerializer):

    #address = ConcreteAddressSerializer()
    class Meta:
        model   = UserProfessionalQual
        fields  = (
                    "profession_type", "profession_type_name", \
                    "institue", "address", "year_start",
                    "in_progress", "year_end"
                 )

    def validate(self, dict_input):
        errors = defaultdict(list)
        profession_type = dict_input["profession_type"]

        if profession_type == UserProfessionalQual.PROF_OTHER:
            try:
                RestAPIAssert(dict_input.get("profession_type_name", None),
                                "Profession-type-name must be passed, for other type of profession",
                                log_msg="Profession-type-name not passed", exception=ValidationError)

            except ValidationError as e:
                errors["profession_type"].append(str(e))

        year_start  = dict_input["year_start"]
        year_end    = dict_input.get("year_end", None)
        in_progress = dict_input.get("in_progress", None)

        request     = self._context["request"]
        user_dob    = request.user.dob
        age         = request.user.age

        current_time = datetime.datetime.now()
        if not user_dob:
            user_dob = relativedelta(current_time, years=age)

        if year_start < user_dob.year:
            errors["year_start"].append("Year-start can't be before user's DOB")

        elif year_start > year_end:
            errors["year_start"].append("Year-end can't be before year-start")

        elif year_end > current_time.year:
            dict_input["in_progress"] = True

        else:
            #   if user have not passed in_progress flag, then
            #   set it false.
            if dict_input.get("in_progress", None) == None:
                dict_input["in_progress"] = False

        if errors:
            raise ValidationError(errors)

        return dict_input

I have defined validate() method in serializer, which performs validations at serializer level(not at field level). Now, the problem is that, for PATCH http method, where only certain fields are involved, it gives keyerror for those fields, which are not part of request body.

What's the best way to write the above validate() method, so that it works in both POST, PUT and PATCH methods ?

Thanks in advance.

Mangu Singh Rajpurohit
  • 10,806
  • 4
  • 68
  • 97
  • When dealing with dictionaries that have differing number of keys present, I always use the `.get()` call. You are intermixing the use of that and direct key accesses in your code. Without knowing exactly which key is throwing the error, the best I can advise is to change all dictionary key accesses to using `.get()`, then wrap those in conditionals to properly handle if they are None. – gallen Feb 14 '17 at 05:33
  • 2
    @Neelik, the problem is not wrt to accessing dictionary. I can definitely use get(). But, my question is wrt. to how to handle patch(), create() and put() methods validations. In create() and put(), all keys are available, but in patch(), some keys may be missing. I can write naive solution for it. But I want to know standard way to do this. – Mangu Singh Rajpurohit Feb 14 '17 at 15:06

2 Answers2

10

Use self.partial in the validate method to find out if it's a partial update.

def validate(self, data):
    if self.partial:
        print("Partial update")
    return data
Dineshs91
  • 2,044
  • 1
  • 25
  • 24
1

you can define validate_< field_name > method to validate specific field, in this way if the field is included in request data, it will be validated; otherwise it will not be validated.

For example:

def validate_year_start(self, value):
    year_start  = value
    request     = self._context["request"]
    user_dob    = request.user.dob
    age         = request.user.age

    current_time = datetime.datetime.now()
    if not user_dob:
        user_dob = relativedelta(current_time, years=age)

    if year_start < user_dob.year:
        raise serializers.ValidationError({"year_start":"Year-start can't be before user's DOB"})
    return value
Emma
  • 136
  • 2
  • 7