related articles:
This chapter is a breakdown of the project. When viewing the content of this chapter, it should be viewed in conjunction with the overall project code:
Python django vue httprunner implements interface automation platform (final version)
1. Interfaces background and related interfaces
request method | URI | corresponding action | Realize function |
GET | /Interfaces/ | .list() | Query the interface list |
POST | /Interfaces/ | .create() | Create a piece of data |
GET | /Interfaces/{id}/ | .retrieve() | Retrieve detailed data of an Interface |
PUT | /Interfaces/{id}/ | update() | Update all fields in a piece of data |
PATCH | /Interfaces/{id}/ | .partial_update() | Update some fields in a piece of data |
DELETE | /Interfaces/{id}/ | .destroy() | delete a piece of data |
GET | /Interfaces/{id}/configs/ | Query the configuration information of an interface | |
POST | /Interfaces/{id}/run/ | Run all cases under a certain interface | |
GET | /Interfaces/{id}/testcases/ | Query the case list under Interface |
Under a project, there will be multiple interfaces.
An interface has multiple cases.
1.1 Query Interface list
GET | /Interfaces/ | .list() | Query the interface list |
Interface list, the interfaces of all projects are in this list.
1.2 Create interface Interface
POST | /Interfaces/ | .create() | Create a piece of data |
1.3 Edit and update interface Interface
PUT | /Interfaces/{id}/ | update() | Update all fields in a piece of data |
Second, the model class model
models.py
from django.db import models
from utils.base_models import BaseModel
class Interfaces(BaseModel):
id = models.AutoField(verbose_name='id主键', primary_key=True, help_text='id主键')
name = models.CharField('接口名称', max_length=200, unique=True, help_text='接口名称')
project = models.ForeignKey('projects.Projects', on_delete=models.CASCADE,
related_name='interfaces', help_text='所属项目')
tester = models.CharField('测试人员', max_length=50, help_text='测试人员')
desc = models.CharField('简要描述', max_length=200, null=True, blank=True, help_text='简要描述')
class Meta:
db_table = 'tb_interfaces'
verbose_name = '接口信息'
verbose_name_plural = verbose_name
ordering = ('id',)
def __str__(self):
return self.name
This code is a Django model class called Interfaces that represents interface information.
This model class inherits from BaseModel and defines the following fields:
- id: Auto-incremented primary key generated automatically.
- name: Interface name, which is a character field with a maximum length of 200 characters and must be unique.
- project: Foreign key field, associated to another model class named Projects, using cascade delete.
- tester: tester, which is a character field with a maximum length of 50.
- desc: Brief description, it is an optional character field, the maximum length is 200, and it is allowed to be empty.
In the Meta inner class of the model class, some metadata is defined:
- db_table: Specifies the name of the database table as 'tb_interfaces'.
- verbose_name: The readable name of the model class is 'interface information', which is used to display on the management interface.
- verbose_name_plural: The plural readable name of the model class is the same as verbose_name.
- ordering: Define the default sorting method as ascending order by id.
Finally, the model class defines a __str__() method that returns the name of the interface (name field value) as a string representation of the instance, which is convenient for debugging and viewing objects.
This model class represents an interface information, including fields such as the name of the interface, the project it belongs to, the tester, and a brief description.
3. Serializer
from rest_framework import serializers
from .models import Interfaces
from projects.models import Projects
class InterfaceModelSerilizer(serializers.ModelSerializer):
project = serializers.StringRelatedField(label='所属项目名称', help_text='所属项目名称')
project_id = serializers.PrimaryKeyRelatedField(label='所属项目id', help_text='所属项目id',
queryset=Projects.objects.all())
class Meta:
model = Interfaces
exclude = ('update_datetime',)
extra_kwargs = {
"create_datetime": {
"read_only": True,
"format": "%Y年%m月%d日 %H:%M:%S"
}
}
def to_internal_value(self, data):
result = super().to_internal_value(data)
result['project'] = result.pop('project_id')
return result
This is a Django REST Framework serializer called InterfaceModelSerializer that is used to serialize and deserialize interface information.
The serializer inherits from ModelSerializer and defines the following fields and metadata:
- project: through serializers.StringRelatedField, the associated project object is serialized into a string representation, and the label is "name of the project to which it belongs".
- project_id: Serialize the associated project object to its primary key ID through serializers.PrimaryKeyRelatedField, the label is "the project id", and the query set is Projects.objects.all().
In the model class, there is no project_id field, only project field. The value obtained for the project field is not passed in by the user, but is queried based on the table information. So we need to manually add the corresponding value of the project field in the serializer.
In the Meta inner class, the following properties are defined:
- model: Specifies that the model corresponding to the serializer is Interfaces.
- exclude: Exclude fields that do not need to be serialized and deserialized, here the update_datetime field is excluded.
- extra_kwargs: used to define additional field parameters, here set the create_datetime field to read-only, and specify the date and time format as "%YYear%mMonth%dDay%H:%M:%S".
In addition, the serializer also rewrites the to_internal_value() method, obtains the deserialized data by calling the method of the parent class, and assigns the value of the project_id field to the project field to match the name in the model class.
The serializer implements serialization and deserialization interface information, and provides some additional field parameters, data conversion and other functions.
Here's why the to_internal_value() method should be rewritten:
result['project'] = result.pop('project_id')
Because in the model class, there is no project_id field, only the project field.
class Interfaces(BaseModel):
id = models.AutoField(verbose_name='id主键', primary_key=True, help_text='id主键')
name = models.CharField('接口名称', max_length=200, unique=True, help_text='接口名称')
project = models.ForeignKey('projects.Projects', on_delete=models.CASCADE,
related_name='interfaces', help_text='所属项目')
tester = models.CharField('测试人员', max_length=50, help_text='测试人员')
desc = models.CharField('简要描述', max_length=200, null=True, blank=True, help_text='简要描述')
The project_id field comes from
3.1 Extension: Rewrite to_internal_value() method
3.1.1. ModelSerializer class introduction:
Five, DRF model serializer ModelSerializer
3.1.2 to_internal_value() original method
Inheritance relationship:
Override the to_internal_value() method, to_internal_value() is inherited from the Serializer class.
source code:
def to_internal_value(self, data):
"""
Dict of native values <- Dict of primitive datatypes.
"""
if not isinstance(data, Mapping):
message = self.error_messages['invalid'].format(
datatype=type(data).__name__
)
raise ValidationError({
api_settings.NON_FIELD_ERRORS_KEY: [message]
}, code='invalid')
ret = OrderedDict()
errors = OrderedDict()
fields = self._writable_fields
for field in fields:
validate_method = getattr(self, 'validate_' + field.field_name, None)
primitive_value = field.get_value(data)
try:
validated_value = field.run_validation(primitive_value)
if validate_method is not None:
validated_value = validate_method(validated_value)
except ValidationError as exc:
errors[field.field_name] = exc.detail
except DjangoValidationError as exc:
errors[field.field_name] = get_error_detail(exc)
except SkipField:
pass
else:
set_value(ret, field.source_attrs, validated_value)
if errors:
raise ValidationError(errors)
return ret
The to_internal_value method is used to convert the incoming raw data data into an internal value, that is, to map the external data data to the attributes of the model.
The incoming raw data data:
- The incoming raw data can be a dictionary type (Mapping), which is a data structure composed of key-value pairs. In this code, determine whether the incoming requirements are met by judging whether the data type is Mapping. If it is not a dictionary type, a validation error will be thrown. The data of the dictionary type can be in the form of {'key': 'value'}, where the key and value can be any legal Python data type.
1. In this method, first determine whether the incoming data is a dictionary type (Mapping). If it is not a dictionary type, a validation error is thrown and an error message is returned. Then, create an ordered dictionary ret for storing converted internal values, and an ordered dictionary errors for storing error messages during validation.
2. Next, get a list of writable fields (_writable_fields) and iterate through each of them. For each field, first try to get the field validation method (validate_method), and then get the value of the original data through the field's get_value(data) method.
3. Next, perform validation and conversion operations on the original value, use the run_validation method of the field to perform validation, and convert the original value to a validated value. If the field defines a validation method (validate_method), this method is called for further processing of the validated value.
During validation, a ValidationError or DjangoValidationError exception may be thrown, indicating that validation failed. At this time, the error information is saved in the errors dictionary, the field name is used as the key, and the error details are used as the value.
If a SkipField exception occurs during the verification process, it means skip the verification operation of this field and directly enter the next field.
Finally, if the errors dictionary contains error information, a ValidationError exception is thrown, which contains validation error information for all fields. If there are no errors, the converted internal value ret is returned.
To sum up, the to_internal_value method is the core method in Django REST Framework for converting external incoming data into internal values. It maps external data to model attributes through validation and conversion operations, and handles errors that may occur during validation.
The return value of the to_internal_value() method:
"""
Dict of native values <- Dict of primitive datatypes.
"""
"Dict of primitive datatypes" refers to a dictionary whose values are data of primitive datatypes. Common primitive data types include integers (int), floating-point numbers (float), strings (string), Boolean values (boolean), and so on.
"Dict of native values" means a dictionary whose values are values of the native data type. In Python, these native data types are consistent with built-in data types, such as int, float, str, bool, etc.
Therefore, "Dict of native values <- Dict of primitive datatypes" means converting the values of primitive datatypes in a dictionary to the values of corresponding native datatypes. This conversion process can be done through Python's built-in functions, such as using methods such as int(), float(), str(), bool(), etc. to convert the value to the corresponding native data type. The end result will be a dictionary with the same keys but values of the native data type.
The return value of the to_internal_value method is usually a dictionary type (Mapping ), representing the converted internal value. In the code, the result variable is a dictionary that stores the processed data.
3.1.3 Rewrite to_internal_value() method
def to_internal_value(self, data):
result = super().to_internal_value(data)
result['project'] = result.pop('project_id')
return result
In this code, the to_internal_value method first calls the to_internal_value method of the parent class and passes in the parameter data. This method is inherited from the parent class, and the default implementation will convert the incoming data into an internal value.
Then, the line of code result['project'] = result.pop('project_id') takes the value whose key is 'project_id' from the dictionary and adds it to the result dictionary with 'project' as the key. At the same time, result.pop('project_id') will delete the key-value pair whose key is 'project_id' in the original dictionary.
Finally, return the processed result dictionary.
The function of this code is to extract the 'project_id' key-value pair from the incoming data, rename it to 'project', and then return the processed dictionary. In this way, the field renaming operation on the data can be realized.
4. View
class InterfaceViewSet(RunMixin, viewsets.ModelViewSet):
queryset = Interfaces.objects.all()
serializer_class = serializers.InterfaceModelSerilizer
permission_classes = [permissions.IsAuthenticated]