Django Rest Framework源碼剖析(六)-----序列化(serializers)

来源:https://www.cnblogs.com/wdliu/archive/2018/06/04/9131500.html
-Advertisement-
Play Games

django rest framework 中的序列化組件,可以說是其核心組件,也是我們平時使用最多的組件,它不僅僅有序列化功能,更提供了數據驗證的功能(與django中的form類似)。 便於展現的序列化操作,我們需要在model添加外鍵、多對多情況。以下是新的models(請刪除原有的資料庫,重 ...


一、簡介

django rest framework 中的序列化組件,可以說是其核心組件,也是我們平時使用最多的組件,它不僅僅有序列化功能,更提供了數據驗證的功能(與django中的form類似)。

便於展現的序列化操作,我們需要在model添加外鍵、多對多情況。以下是新的models(請刪除原有的資料庫,重新migrate):

models.py

from django.db import models

class UserInfo(models.Model):
    user_type_choice = (
        (1,"普通用戶"),
        (2,"會員"),
    )
    user_type = models.IntegerField(choices=user_type_choice)
    username = models.CharField(max_length=32,unique=True)
    password = models.CharField(max_length=64)
    group = models.ForeignKey(to='UserGroup',null=True,blank=True)
    roles = models.ManyToManyField(to='Role')


class UserToken(models.Model):
    user = models.OneToOneField(to=UserInfo)
    token = models.CharField(max_length=64)



class UserGroup(models.Model):
    """用戶組"""
    name = models.CharField(max_length=32,unique=True)


class Role(models.Model):
    """角色"""
    name = models.CharField(max_length=32,unique=True)
二、使用

1.基本使用

在urls.py中添加新的角色url,以前的url為了減少干擾,在這裡進行註釋:

from django.conf.urls import url
from app01 import views

urlpatterns = [

    # url(r'^api/v1/auth', views.AuthView.as_view()),
    # url(r'^api/v1/order', views.OrderView.as_view()),
    url(r'^api/v1/roles', views.RoleView.as_view()),  # 角色視圖
    # url(r'^api/(?P<version>[v1|v2]+)/user', views.UserView.as_view(),name="user_view"),
]

views.py

from rest_framework import serializers
from rest_framework.views import APIView
from django.shortcuts import  HttpResponse
from  app01 import  models
import json


class RolesSerializer(serializers.Serializer): #定義序列化類
    id=serializers.IntegerField()  #定義需要提取的序列化欄位,名稱和model中定義的欄位相同
    name=serializers.CharField()
class RoleView(APIView):
    """角色"""
    def get(self,request,*args,**kwargs):
        roles=models.Role.objects.all()
        res=RolesSerializer(instance=roles,many=True) #instance接受queryset對象或者單個model對象,當有多條數據時候,使用many=True,單個對象many=False
        return HttpResponse(json.dumps(res.data,ensure_ascii=False))

使用瀏覽器訪問http://127.0.0.1:8000/api/v1/roles,結果如下:

2.自定義序列化欄位

當數據模型中有外鍵或者多對多時候,這時候就需要自定義序列化了

新增用戶信息url

from django.conf.urls import url
from app01 import views

urlpatterns = [

    # url(r'^api/v1/auth', views.AuthView.as_view()),
    # url(r'^api/v1/order', views.OrderView.as_view()),
    url(r'^api/v1/roles', views.RoleView.as_view()),
    url(r'^api/v1/userinfo', views.UserinfoView.as_view()), #用戶信息
    # url(r'^api/(?P<version>[v1|v2]+)/user', views.UserView.as_view(),name="user_view"),
]

UserinfoView和序列化類

from rest_framework import serializers
from rest_framework.views import APIView
from django.shortcuts import  HttpResponse
from  app01 import  models
import json


class UserinfoSerializer(serializers.Serializer): #定義序列化類
    id=serializers.IntegerField()  #定義需要提取的序列化欄位,名稱和model中定義的欄位相同

    username=serializers.CharField()
    password=serializers.CharField()
    #sss=serializers.CharField(source='user_type') #該方法只能拿到user_type的ID
    sss=serializers.CharField(source='get_user_type_display') #自定義欄位名稱,和數據模型不一致,需要指定source本質調用get_user_type_display()方法獲取數據
    gp=serializers.CharField(source='group.name') #本質拿到group對象,取對象的name,
    #rl=serializers.CharField(source='roles.all.first.name')
    rl=serializers.SerializerMethodField()   #多對多序列化方法一
    def get_rl(self,obj): #名稱固定:get_定義的欄位名稱
        """
        自定義序列化
        :param obj:傳遞的model對象,這裡已經封裝好的
        :return:
        """
        roles=obj.roles.all().values() #獲取所有的角色

        return list(roles)  #返回的結果一定有道是json可序列化的對象
class UserinfoView(APIView):
    """用戶信息"""
    def get(self,request,*args,**kwargs):
        users=models.UserInfo.objects.all()
        res=UserinfoSerializer(instance=users,many=True) #instance接受queryset對象或者單個model對象,當有多條數據時候,使用many=True,單個對象many=False
        return HttpResponse(json.dumps(res.data,ensure_ascii=False))

訪問http://127.0.0.1:8000/api/v1/userinfo,查看結果:

除了以上的Serializer,還可以使用ModelSerializer,ModelSerializer繼承了serializer,其結果和上面示例一樣:

class UserinfoSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField()  # 定義需要提取的序列化欄位,名稱和model中定義的欄位相同
    username=serializers.CharField()
    password=serializers.CharField()
    #sss=serializers.CharField(source='user_type') #該方法只能拿到user_type的ID
    sss=serializers.CharField(source='get_user_type_display') #自定義欄位名稱,和數據模型不一致,需要指定source本質調用get_user_type_display()方法獲取數據
    #rl=serializers.CharField(source='roles.all.first.name')
    gp=serializers.CharField(source='group.name')
    rl=serializers.SerializerMethodField()   #多對多序列化方法一
    def get_rl(self,obj): #名稱固定:get_定義的欄位名稱
        """
        自定義序列化
        :param obj:傳遞的model對象,這裡已經封裝好的
        :return:
        """
        roles=obj.roles.all().values() #獲取所有的角色

        return list(roles)  #返回的結果一定有道是json可序列化的對象
    class Meta:
        model = models.UserInfo
        fields = ['id', 'username', 'password', 'sss','rl','gp'] #配置要序列化的欄位
        # fields = "__all__" 使用model中所有的欄位

class UserinfoView(APIView):
    """用戶信息"""
    def get(self,request,*args,**kwargs):
        users=models.UserInfo.objects.all()
        res=UserinfoSerializer(instance=users,many=True) #instance接受queryset對象或者單個model對象,當有多條數據時候,使用many=True,單個對象many=False
        return HttpResponse(json.dumps(res.data,ensure_ascii=False))

3.連表序列化以及深度控制

使用depth進行深度控制,越深其序列化的細讀越高

class UserinfoSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.UserInfo
        #fields = "__all__" # 使用model中所有的欄位
        fields = ['id', 'username', 'password', 'group','roles']  # 配置要序列化的欄位
        depth = 1  #系列化深度,1~10,建議使用不超過3
class UserinfoView(APIView):
    """用戶信息"""
    def get(self,request,*args,**kwargs):
        users=models.UserInfo.objects.all()
        res=UserinfoSerializer(instance=users,many=True) #instance接受queryset對象或者單個model對象,當有多條數據時候,使用many=True,單個對象many=False
        return HttpResponse(json.dumps(res.data,ensure_ascii=False))

請求http://127.0.0.1:8000/api/v1/userinfo,結果如下:

 

4.序列化欄位url

urls.py新加入組url

urlpatterns = [

    # url(r'^api/v1/auth', views.AuthView.as_view()),
    # url(r'^api/v1/order', views.OrderView.as_view()),
    url(r'^api/v1/roles', views.RoleView.as_view()),
    url(r'^api/v1/userinfo', views.UserinfoView.as_view()),
    url(r'^api/v1/group/(?P<xxx>\d+)', views.GroupView.as_view(),name='gp'),  # 新加入組url
    # url(r'^api/(?P<version>[v1|v2]+)/user', views.UserView.as_view(),name="user_view"),
]

 

views.py

class UserinfoSerializer(serializers.ModelSerializer):
    group=serializers.HyperlinkedIdentityField(view_name='gp',lookup_field='group_id',lookup_url_kwarg='xxx')
    #view_name,urls.py目標url的視圖別名(name),這裡是UserGroup的視圖別名
    #lookup_field 給url傳遞的參數,也就是正則匹配的欄位
    #lookup_url_kwarg,url中正則名稱,也就是kwargs中的key
    class Meta:
        model = models.UserInfo
        #fields = "__all__" # 使用model中所有的欄位
        fields = ['id', 'username', 'password','roles','group']  # 配置要序列化的欄位
        depth = 1  #系列化深度,1~10,建議使用不超過3
class UserinfoView(APIView):
    """用戶信息"""
    def get(self,request,*args,**kwargs):
        users=models.UserInfo.objects.all()
        res=UserinfoSerializer(instance=users,many=True,context={'request': request}) #instance接受queryset對象或者單個model對象,當有多條數據時候,使用many=True,單個對象many=False
        #若需生成超鏈接欄位,則需要加context={'request': request}
        return HttpResponse(json.dumps(res.data,ensure_ascii=False))

class UserGroupSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserGroup
        fields = "__all__"
        depth = 0


class GroupView(APIView):
    def get(self,request,*args,**kwargs):

        group_id=kwargs.get('xxx')
        group_obj=models.UserGroup.objects.get(id=group_id)
        res=UserGroupSerializer(instance=group_obj,many=False) #instance接受queryset對象或者單個model對象,當有多條數據時候,使用many=True,單個對象many=False
        return HttpResponse(json.dumps(res.data,ensure_ascii=False))

此時訪問組信息:http://127.0.0.1:8000/api/v1/group/1,結果如下:

在查看用戶信息,此時生成的組就是超鏈接形式了(便於查看json數據,這裡用postman發請求):

 

三、源碼剖析

1.類的基本知識
  • 類是實例化之前會執行__new__方法,用於控制一個類生成實例的過程

  • 子類沒有__new__方法執行父類的__new__方法
  • __new__方法執行完以後執行__init__構造方法

2.以ModelSerializer為例,無__new__方法,其父類Serializer也沒有,在往上父類BaseSerializer中含有__new__方法,分析請看註釋,下麵是源碼部分:

class BaseSerializer(Field):
    """
    The BaseSerializer class provides a minimal class which may be used
    for writing custom serializer implementations.

    Note that we strongly restrict the ordering of operations/properties
    that may be used on the serializer in order to enforce correct usage.

    In particular, if a `data=` argument is passed then:

    .is_valid() - Available.
    .initial_data - Available.
    .validated_data - Only available after calling `is_valid()`
    .errors - Only available after calling `is_valid()`
    .data - Only available after calling `is_valid()`

    If a `data=` argument is not passed then:

    .is_valid() - Not available.
    .initial_data - Not available.
    .validated_data - Not available.
    .errors - Not available.
    .data - Available.
    """

    def __init__(self, instance=None, data=empty, **kwargs): # many=False後執行的構造方法
        self.instance = instance
        if data is not empty:
            self.initial_data = data
        self.partial = kwargs.pop('partial', False)
        self._context = kwargs.pop('context', {})
        kwargs.pop('many', None)
        super(BaseSerializer, self).__init__(**kwargs)

    def __new__(cls, *args, **kwargs):
        # We override this method in order to automagically create
        # `ListSerializer` classes instead when `many=True` is set.
        if kwargs.pop('many', False):    # many參數,如果有則執行cls.many_init,沒有則執行super(BaseSerializer).__new__
            return cls.many_init(*args, **kwargs)  # many=True,表示對QuerySet進行處理,走該邏輯,
        return super(BaseSerializer, cls).__new__(cls, *args, **kwargs) # many = False ,表示對單獨的對象進行處理

執行玩__new__方法接著執行__init__構造方法,此時有根據many值不同執行不同的構造方法,當many=True時候執行cls.many_init方法,

@classmethod
    def many_init(cls, *args, **kwargs):  # many=True,執行該方法
        """
        This method implements the creation of a `ListSerializer` parent
        class when `many=True` is used. You can customize it if you need to
        control which keyword arguments are passed to the parent, and
        which are passed to the child.

        Note that we're over-cautious in passing most arguments to both parent
        and child classes in order to try to cover the general case. If you're
        overriding this method you'll probably want something much simpler, eg:

        @classmethod
        def many_init(cls, *args, **kwargs):
            kwargs['child'] = cls()
            return CustomListSerializer(*args, **kwargs)
        """
        allow_empty = kwargs.pop('allow_empty', None)
        child_serializer = cls(*args, **kwargs)
        list_kwargs = {
            'child': child_serializer,
        }
        if allow_empty is not None:
            list_kwargs['allow_empty'] = allow_empty
        list_kwargs.update({
            key: value for key, value in kwargs.items()
            if key in LIST_SERIALIZER_KWARGS
        })
        meta = getattr(cls, 'Meta', None)
        list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
        return list_serializer_class(*args, **list_kwargs)  # 最後使用ListSerializer進行實例化

從上面源碼中我們知道,對於單獨的對象,採用的是Serializer類進行處理,若對象是QuerySet類型(多個對象列表),採用LIstSeriallizer處理,此時我們調用對象的data屬性獲取結果(示例中這使用的是res.data),下麵是源碼(尋找時候先從子類找,無該屬性就去父類找):

   @property
    def data(self):
        ret = super(Serializer, self).data  # 執行父類data屬性
        return ReturnDict(ret, serializer=self)

 父類BaseSerialize的屬性方法data源碼:

 @property
    def data(self):
        if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'):  #數據驗證時候使用
            msg = (
                'When a serializer is passed a `data` keyword argument you '
                'must call `.is_valid()` before attempting to access the '
                'serialized `.data` representation.\n'
                'You should either call `.is_valid()` first, '
                'or access `.initial_data` instead.'
            )
            raise AssertionError(msg)

        if not hasattr(self, '_data'):    
            if self.instance is not None and not getattr(self, '_errors', None):# 判斷有無錯誤,無錯誤進行序列化
                self._data = self.to_representation(self.instance)    # 將instance(QuerySet對象)傳入,開始序列化
            elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
                self._data = self.to_representation(self.validated_data) 
else: self._data = self.get_initial() return self._data

從以上源碼中可以看出,序列化方法是通過調用類的self.to_representation方法進行序列化,下麵我們看Serializer類的to_representation方法

    def to_representation(self, instance):
        """
        Object instance -> Dict of primitive datatypes.
        """
        ret = OrderedDict()  #先將instance轉化為有序字典
        fields = self._readable_fields

        for field in fields: # 迴圈定義的欄位,這個欄位可以是我們自己定義的,也可以是model中的欄位
            try:
                attribute = field.get_attribute(instance) #調用欄位的get_attribute方法(參數是對象),在示例中可以理解為group.get_attribute(group_obj),
            except SkipField:
                continue

            # We skip `to_representation` for `None` values so that fields do
            # not have to explicitly deal with that case.
            #
            # For related fields with `use_pk_only_optimization` we need to
            # resolve the pk value.
            check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
            if check_for_none is None:
                ret[field.field_name] = None
            else:
                ret[field.field_name] = field.to_representation(attribute)

        return ret

以上源碼中,調用field.get_attribute(instance)方法獲取每個欄位的數據,下麵是field.get_attribute(instance)源碼(在Field中)

    def get_attribute(self, instance):
        """
        Given the *outgoing* object instance, return the primitive value
        that should be used for this field.
        """
        try:
        
return get_attribute(instance, self.source_attrs) # 執行get_attribute函數,用於根據定義的欄位屬性,獲取不同的數據,
                                       註意該方法沒有帶self,是一個函數,並不是類方法。
        self.source_attrs:以'.'分割的列表,會被使用為反射獲取屬性
except (KeyError, AttributeError) as exc: if self.default is not empty: return self.get_default() if self.allow_null: return None if not self.required: raise SkipField() msg = ( 'Got {exc_type} when attempting to get a value for field ' '`{field}` on serializer `{serializer}`.\nThe serializer ' 'field might be named incorrectly and not match ' 'any attribute or key on the `{instance}` instance.\n' 'Original exception text was: {exc}.'.format( exc_type=type(exc).__name__, field=self.field_name, serializer=self.parent.__class__.__name__, instance=instance.__class__.__name__, exc=exc ) ) raise type(exc)(msg)

調用get_attribute函數,進一步出來,需要分析self.source_attrs參數,下麵是self.source_attrs部分源碼:

        if self.source == '*':   
            self.source_attrs = []
        else:
            self.source_attrs = self.source.split('.')  
#self.source是我們自定義欄位傳入的source參數,如:gp=serializers.CharField(source='group.name'),sss=serializers.CharField(source='get_user_type_display')
最後分割變成['group','name']

以上分析self.source_attrs是一個列表(由source參數按點分割而來),繼續回到get_attribute函數,下麵是其源碼:

def get_attribute(instance, attrs):
    """
    Similar to Python's built in `getattr(instance, attr)`,
    but takes a list of nested attributes, instead of a single attribute.

    Also accepts either attribute lookup on objects or dictionary lookups.
    """

# attrs:['group','name']或者['get_user_type_display',] for attr in attrs: # 迴圈列表 try: if isinstance(instance, collections.Mapping): #若果是model欄位映射(DRF的內部欄位轉化),直接調用model類的 instance = instance[attr]#重新賦值,此時的instance已經改變 else: instance = getattr(instance, attr) #否則,使用反射獲取結果,如instance=getattr(userinfo_obj,group) except ObjectDoesNotExist: return None if is_simple_callable(instance): #判斷是否是可執行,此時如我們示例中的get_user_type_display,其判斷過程在類似下麵TIPS中,這裡不再做過多說明 try: instance = instance() #重新賦值,加括弧進行執行 except (AttributeError, KeyError) as exc: # If we raised an Attribute or KeyError here it'd get treated # as an omitted field in `Field.get_attribute()`. Instead we # raise a ValueError to ensure the exception is not masked. raise ValueError('Exception raised in callable attribute "{0}"; original exception was: {1}'.format(attr, exc)) return instance

TIPS:判斷是否是可執行方法

import types

def func(arg):
    if isinstance(arg,types.FunctionType,):
        print('yes')
        arg()
    else:
        print('NO')

func(lambda :1)
func(111)

#執行結果:
yes
NO

從上面的源碼分析來看,序列化本質是使用了django的orm的QuerSet或者單個model對象特性,利用反射或者方法進行序列化。

四、數據驗證

1.基本驗證

DRF的數據驗證功能與django的form有點類似,示例:獲取數據使用的是全局配置的json解析器,在解析器中已經介紹:

class CheckGroupData(serializers.Serializer):
    id=serializers.IntegerField(error_messages={'required':'id不能為空'})
    name=serializers.CharField(error_messages={'required':'組名不能為空'})
class GroupView(APIView):
    def get(self,request,*args,**kwargs):

        group_id=kwargs.get('xxx')
        group_obj=models.UserGroup.objects.get(id=group_id)
        res=UserGroupSerializer(instance=group_obj,many=False) #instance接受queryset對象或者單個model對象,當有多條數據時候,使用many=True,單個對象many=False
        return HttpResponse(json.dumps(res.data,ensure_ascii=False))

    def post(self,request,*args,**kwargs):
        ret=CheckGroupData(data=request.data)#這裡配置了全局json解析器,使用request.data直接獲取數據
        if ret.is_valid():
            print(ret.validated_data)
            #獲取某個欄位數據ret.validated_data.get('name')
            return HttpResponse('數據驗證成功')
        else:
            print(ret.errors)
            return HttpResponse('數據驗證失敗')

使用postman向http://127.0.0.1:8000/api/v1/group/1,發送json數據,結果如下:

後臺結果:

驗證流程生效。

2.自定義驗證

和django form功能一樣,DRF序列化支持自定義數據驗證,示例:

#自定義驗證規則
class MyValidation(object):
    def __init__(self,base):
        self.base = base

    def __call__(self, value): #value是欄位值,預設傳遞
        if value == 'wd':
            message = "關鍵字%s不能是%s"%(self.base,value)
            raise serializers.ValidationError(message)


class MySerializer(serializers.Serializer):
    name = serializers.CharField(validators=[MyValidation(base='name_field'),])



class GroupView(APIView):
    def get(self,request,*args,**kwargs):

        group_id=kwargs.get('xxx')
        group_obj=models.UserGroup.objects.get(id=group_id)
        res=UserGroupSerializer(instance=group_obj,many=False) #instance接受queryset對象或者單個model對象,當有多條數據時候,使用many=True,單個對象many=False
        return HttpResponse(json.dumps(res.data,ensure_ascii=False))

    def post(self,request,*args,**kwargs):
        ret=MySerializer(data=request.data)#這裡配置了全局json解析器,使用request.data直接獲取數據
        if ret.is_valid():
            print(ret.validated_data)
            #獲取某個欄位數據ret.validated_data.get('name')
            return HttpResponse('數據驗證成功')
        else:
            print(ret.errors)
            return HttpResponse('數據驗證失敗')

發送{"name":"wd"}數據進行驗證結果如下:

3.鉤子函數

對於自定義驗證來說,DRF和django的form組件一樣也給我們內置了鉤子函數,用於驗證。

驗證流程:

is_valid-->self.run_validation-->to_internal_value-->to_internal_value-->validate_欄位名稱(執行欄位驗證,鉤子方法)-->validate_method(鉤子驗證方法)

validate_欄位名稱鉤子方法驗證示例:

class MySerializer(serializers.Serializer):
    name = serializers.CharField()

    def validate_name(self,value): # 驗證的欄位值
        if value.startswith("w"):
            raise serializers.ValidationError('name欄位不能以w開頭')
        else:
            return value #註意通過驗證,必須返回其值

class GroupView(APIView):
    def get(self,request,*args,**kwargs):

        group_id=kwargs.get('xxx')
        group_obj=models.UserGroup.objects.get(id=group_id)
        res=UserGroupSerializer(instance=group_obj,many=False) #instance接受queryset對象或者單個model對象,當有多條數據時候,使用many=True,單個對象many=False
        return HttpResponse(json.dumps(res.data,ensure_ascii=False))

    def post(self,request,*args,**kwargs):
        ret=MySerializer(data=request.data)#這裡配置了全局json解析器,使用request.data直接獲取數據
        if ret.is_valid():
            print(ret.validated_data)
            #獲取某個欄位數據ret.validated_data.get('name')
            return HttpResponse('數據驗證成功')
        else:
            print(ret.errors)
            return HttpResponse('數據驗證失敗')

同樣發送json數據{"name":"wd"}進行驗證,結果如下:

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • lesson1 <!DOCTYPE html><html lang="en,zh"><!-- 告訴搜索引擎爬蟲,我們的網站是關於什麼內容的 --><head> <meta charset="utf-8"> <meta content="服裝" name="keywords"> <meta conte ...
  • 下麵是幾種方法的公用部分(右自適應也是一樣的,換一下方向) html: css: 1. 左脫離文檔流,右margin 1.1 左float 1.2 左absolute 2. flex佈局(左定寬,右flex設置為1,自動伸展,此處註意flex相容性,可以通postcss等後處理器自動加上首碼) 效果 ...
  • 單例設計模式 目的:讓類創建對象,在系統中只有唯一的實例,讓每一次創建的對象返回的記憶體地址都是相同的。 __new__方法 使用類名創建對象時,python解釋器首先會調用__new__方法為對象分配空間 __new__方法是有object基類提供的靜態方法,主要作用有兩個: 為記憶體中的對象分配空間 ...
  • 最近想系統學習並使用一下boost的asio非同步網路庫,所以需要編譯boost庫使用,下麵簡單介紹如何編譯。 ...
  • 今天介紹的專題是servlet和jsp,屬於web開發中的基礎,先來實際操作一下servlet,創建你的第一個web小項目。 1、新建一個maven項目。 滑鼠右鍵單擊eclipse左邊項目欄的空白區,選擇第一個new,在出現的下拉中選擇other,在新彈出的框中選擇maven,點開maven選擇m ...
  • 很多寫C/C++的人都知道“記憶體對齊”的概念以及規則,但不一定對他有很深入的瞭解。這篇文章試著從硬體到C++語言、更徹底地講一下C++的記憶體對齊。 ...
  • Java開源生鮮電商平臺-銷售管理設計與架構(源碼可下載) 說明:在Java開源生鮮電商平臺中,銷售人員我們稱為跟餐飲店老闆溝通與下載APP的一類地推人員。(所謂地推指的就是一個一個上門拜訪。) 由於銷售人員有以下幾類特性: 1. 時間隨意性,他們並不類似技術或者性質人員,需要天天呆在辦公室,他們是 ...
  • Q: 為什麼要引入鏈表的概念?它是解決什麼問題的? A: 數組作為數據存儲結構有一定的缺陷,在無序數組中,搜索是低效的;而在有序數組中,插入效率又很低;不管在哪一個數組中刪除效率都很低;況且一個數組創建後,它的大小是不可改變的。 A: 在本篇中,我們將學習一種新的數據結構 —— 鏈表,它可以解決上面 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...