(待更)DRF: 序列化器、View、APIView、GenericAPIView、Mixin、ViewSet、ModelViewSet的源码解析

news2024/11/20 3:31:13

前言:还没有整理,后续有时间再整理,目前只是个人思路,文章较乱。

注意路径匹配的“/”

我们的url里面加了“/”,但是用apifox等非浏览器的工具发起请求时没有加“/”,而且还不是get请求,那么这个请求就会被加上“/”且重定向成一个get请求。从而导致返回None且报错。
譬如现在有个视图类:

class BookView(APIView):
    def get(self, request):
        return Response("get success")

    def post(self, request):
        return Response("post success")

urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('book/', views.BookView.as_view())
]

在这里插入图片描述
可以发现如果走DELETE方法,它结果返回的是get请求中的内容。

但是为什么发送POST请求的时候,它不会返回的是get请求中的内容,反而报错如下呢?
在这里插入图片描述

翻译为:你通过 POST 方法调用了这个 URL,但是 URL 没有以斜杠结尾,同时你的 APPEND_SLASH 设置为开启状态。Django 无法在保持 POST 数据的情况下重定向到带斜杠的 URL。请更改你的表单,使其指向 localhost:8000/book/(注意尾部的斜杠),或者在你的 Django 设置中将 APPEND_SLASH 设置为 False。

实际上这个重定向的设置和我们django的默认设置global_settings里面的APPEND_SLASH = True有关。
官方关于他的解释如下:

APPEND_SLASH
Default: True

When set to True, if the request URL does not match any of the patterns in the URLconf and it doesn't end in a slash, an HTTP redirect is issued to the same URL with a slash appended. Note that the redirect may cause any data submitted in a POST request to be lost.

The APPEND_SLASH setting is only used if CommonMiddleware is installed (see 中间件). See also PREPEND_WWW.

所以当请求是POST时不会像DELETE一样进行重定向,因为很有可能损失post里面携带的数据。

APIView源码解析:

当我们启动django框架时,django开始。
当我们匹配到相应路由时,先去BookView里面找as_view方法,发现没有找到,所以去BookView继承的APIView中找。

其中classmethod表示这是一个类方法,不是一个类实例化对象方法。
cls表示的是哪个类调用的这个方法,谁就是cls。
他和self不同的是self精确到了哪个类的实例化对象调用的这个方法,谁就是self。
所以这里的cls就是BookView
self就是BookView的一个实例化对象本身。
其中as_view最核心的代码如下:

class APIView(View):
    @classmethod
    def as_view(cls, **initkwargs):
        view = super().as_view(**initkwargs)
        view.cls = cls
        return view

可以看到APIView的as_view()实际上走了其父类View的as_view(),结果也相当于父类as_view方法的结果。
所以我们把View的as_view()拿过来。

@classonlymethod
    def as_view(cls, **initkwargs):
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs
        return view

可以看到as_view的结果就是view这个方法。
也就是说:views.BookView.as_view()➡️View.as_view()➡️View.view
而且!view这个方法的第一件事,就是实例化一个对象。
现在是BookView这个实例化对象调用到了View层的view方法,然后self = cls(**initkwargs)创建了BookView的对象赋值给了self。然后再返回了

as_view 方法只是构建了 view 函数,并设置了必要的属性和注解,然后返回这个函数。

    def dispatch(self, request, *args, **kwargs):
        if request.method.lower() in self.http_method_names:
            handler = getattr(
                self, request.method.lower()       )

意思就是views.BookView.as_view()➡️View.as_view()➡️View.view➡️View.view()➡️View.dispatch()➡️BookeView.get()
从而实现转发。

views.BookView.as_view()➡️View.as_view()➡️View.view➡️View.view()➡️APIView.dispatch()➡️BookeView.get()
从而实现转发。

 def dispatch(self, request, *args, **kwargs):
		# 构建一个新的request对象,不再是django原生的request。
        self.args = args
        self.kwargs = kwargs
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        
        try:
        	# 认证、权限、限流等 
            self.initial(request, *args, **kwargs)

            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            response = handler(request, *args, **kwargs)

        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

为什么APIView需要构建一个新的request对象?因为django原生的request对于解析POST请求的数据很有局限性,例如前端发送的数据有多种格式时,譬如有urlencoded和json格式时,django原生的request.POST只能解析并携带urlencoded格式的数据。而drf的request则不一样,它的request.body支持解析并携带各种格式的数据。而drf的request的_request实际上就是django原生的request。

注意序列化器相关:

还是书本的那个视图。
当我post请求时,我需要先验证前端传来的数据,转化成模型对象插入到数据库中,然后我返回校验后的数据。
models.py

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="书籍名称")
    price = models.IntegerField(verbose_name="价格")
    pub_date = models.DateField(verbose_name="出版日

serializers.py


class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        serializer = BookSerializer(instance=book_list, many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = BookSerializer(data=request.data)
        if serializer.is_valid():
            Book.objects.create(**serializer.validated_data)
            return Response(serializer.data)
        else:
            return Response(serializer.errors)

虽然从本质上,我的序列化器的代码和我的模型代码没有关联性,但是在代码逻辑上,这两者一定是强相关的。所以如果一个字段我在序列化器中要求他可以为非必需的字段,那么前端就有可能传空值过来。此时在模型代码中又没有把对应的字段设置为null=True,那么就会导致存储到数据库时报错如下:
在这里插入图片描述

所以一般序列化器中设置为required=False的字段在模型中也都是null=True,反之也是。

所以这里代码要加上null=True
注意还要先运行数据库迁移的migrate等操作,才能再次启动服务器发请求。

如上代码还是有一定问题的,就是在视图函数中其实只应该做数据校验、返回响应之类的操作,数据库里新增数据等orm操作应该解耦出去,不应该绑定在数据校验的if else分支里面。所以序列化器中定义了一个叫save1的方法。

    def post(self, request):
        serializer = BookSerializer(data=request.data)
        if serializer.is_valid():
            # Book.objects.create(**serializer.validated_data)
            serializer.save()
            return Response(serializer.data)
        else:
            return Response(serializer.errors)

serialize.save()首先去找的是BookSerializer中是否有save方法,发现没有。于是去serializers.Serializer里面找,发现也没有,于是去BaseSerializer中找,然后发现找到了。

class BaseSerializer(Field):
    def save(self, **kwargs):
        validated_data = {**self.validated_data, **kwargs}

        if self.instance is not None:
            self.instance = self.update(self.instance, validated_data)
            assert self.instance is not None, (
                '`update()` did not return an object instance.'
            )
        else:
            self.instance = self.create(validated_data)
            assert self.instance is not None, (
                '`create()` did not return an object instance.'
            )

        return self.instance

在这里发现做了个判断instance属性是否为空。
因为我们在视图函数中创建serializer对象时用的是serializer = BookSerializer(data=request.data),注入的是data属性不是instance属性,所以这里肯定走if为空的判断,然后发现调用了create方法。
那么首先肯定还是看BookSerializer中是否有create方法,发现没有。于是去serializers.Serializer里面找,发现也没有,于是去BaseSerializer中找,然后发现找到了。

    def create(self, validated_data):
        raise NotImplementedError('`create()` must be implemented.')

也就是说其实我们的BookSerializer应该重写这个方法,不重写的话,现在请求会报错说create() must be implemented.
于是我们重写,而且应该返回数据,从而实现解耦。

    def create(self, validated_data):
        return Book.objects.create(**validated_data)

或者

    def create(self, validated_data):
        return Book.objects.create(**self.validated_data)

且虽然是一个资源,但建议路径中有参数的化为一个单独的视图类。譬如BookView和BookDetailView。

class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        serializer = BookSerializer(instance=book_list, many=True)
        return Response(serializer.data)

    def put(self, request, id):
        book = Book.objects.get(pk=id)
        serializer = BookSerializer(instance=book, data=request.data)
        if serializer.is_valid():
            Book.objects.filter(pk=id).update(**serializer.validated_data)
            return Response(serializer.data)

现在来看一下serializer.data。

    @property
    def data(self):
        ret = super().data
        return ReturnDict(ret, serializer=self)

可以看到实际上这是一个data方法。
其父类的data方法为:

@property
    def data(self):
        if not hasattr(self, '_data'):
            if self.instance is not None and not getattr(self, '_errors', None):
                self._data = self.to_representation(self.instance)
            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

这几个判断,第一个判断的意思是如果instance属性不为空且_errors属性为空。那么_data属性就是instance属性数据序列化后的内容。
也就是说每次我们序列化返回响应时,实际上就在返回序列化后的instance属性数据。
那么关于普通的get、post、delete方法,都没什么毛病。但是对于更新put方法。因为我们创建serializer时用的是:

serializer = BookSerializer(instance=book, data=request.data)

按理来说返回的响应内容应该是更新后的数据,
但实际上返回的是更新前的数据,虽然更新操作做了,但是响应不是我们想要的。所以我们应该写成如下:

        if serializer.is_valid():
            Book.objects.filter(pk=id).update(**serializer.validated_data)
            updated_book = Book.objects.get(pk=id)
            serializer.instance = updated_book
            return Response(serializer.data)

手动指定下instance为更新后的数据。
这里不写成

		updated_book = Book.objects.filter(pk=id).update(**serializer.validated_data)
        serializer.instance = updated_book
        return Response(serializer.data)

是因为update方法返回的数据不是updated_book,而是修改了多少条记录的一个int值。

那么关于post方法,明明创建serializer时没有给她的instance属性赋值,那么为什么最终还是可以正常返回data?
那是因为在save()方法里面

    def save(self, **kwargs):
        validated_data = {**self.validated_data, **kwargs}

        if self.instance is not None:
            self.instance = self.update(self.instance, validated_data)
            assert self.instance is not None, (
                '`update()` did not return an object instance.'
            )
        else:
            self.instance = self.create(validated_data)
            assert self.instance is not None, (
                '`create()` did not return an object instance.'
            )

        return self.instance

其中最关键的是self.instance = self.create(validated_data)
也就是说create方法返回的值赋值给了instance。

当然,为了解耦,我们实际上还是要把更新数据的逻辑暴露出去的。

class BookSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    price = serializers.IntegerField(required=False)
    pub_date = serializers.DateField()

    def create(self, validated_data):
        return Book.objects.create(**validated_data)

    def update(self, instance, validated_data):
        Book.objects.filter(pk=instance.pk).update(**validated_data)
        updated_book = Book.objects.get(pk=instance.pk)
        return updated_book
    def put(self, request, id):
        book = Book.objects.get(pk=id)
        serializer = BookSerializer(instance=book, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)

ModelSerializer

序列化器可以升级为modelSerializer
此时不仅代码逻辑上强相关,代码语法上也完全强相关了。

class BookSerializer(serializers.ModelSerializer):
    date = serializers.DateField(source="pub_date")
    class Meta:
        model = Book
        exclude = ['pub_date']

GenericAPIView

如果此时新增一个资源,完成相应视图。可以发现每个视图的方法涉及到的变量就是模型和序列化器。
所以GenericAPIView出现了。

class BookView(GenericAPIView):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    def get(self, reverse, *args, **kwargs):
        serializer = self.get_serializer(instance=self.get_queryset(), many=True)
        return Response(serializer.data)
class GenericAPIView(views.APIView):
    queryset = None
    serializer_class = None
    
    lookup_field = 'pk'
    lookup_url_kwarg = None

    def get_queryset(self):

        assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )

        queryset = self.queryset
        if isinstance(queryset, QuerySet):
            queryset = queryset.all()
        return queryset
    def get_serializer(self, *args, **kwargs):

        serializer_class = self.get_serializer_class()
        kwargs.setdefault('context', self.get_serializer_context())
        return serializer_class(*args, **kwargs)

    def get_object(self):
        queryset = self.filter_queryset(self.get_queryset())

        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)

        # May raise a permission denied
        self.check_object_permissions(self.request, obj)

        return obj

    def get_serializer_class(self):
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )

        return self.serializer_class

可以看到其实

    def get(self, reverse, *args, **kwargs):
        # serializer = self.get_serializer(instance=self.get_queryset(), many=True)
        serializer = self.get_serializer_class()(instance=self.get_queryset(), many=True)
        return Response(serializer.data)

是一样的。

现在来仔细看下get_object()方法:


    def get_object(self):
        queryset = self.filter_queryset(self.get_queryset())

        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)

        # May raise a permission denied
        self.check_object_permissions(self.request, obj)

        return obj

这里filter_kwargs取决于lookup_field是什么。
点进去发现默认为lookup_field = ‘pk’
这就意味着我们的url需要改为

path('sers/book/<int:pk>/', views.BookDetailView.as_view())

否则报错。如果我们坚持想要用id
那么就需要:

class BookDetailView(GenericAPIView):
    serializer_class = BookSerializer
    queryset = Book.objects.all()
    lookup_field = 'id'

    def get(self, request, id):
        serializer = self.get_serializer(instance=self.get_object(), many=False)
        return Response(serializer.data)
    path('sers/book/<int:id>/', views.BookDetailView.as_view())

Mixin

此时视图类中的各个方法的代码就固定了。DRF再封装了一层,将各个固定的处理对应请求的逻辑分别封装到五个Mixin类中。

我们的视图函数只要利用多继承同时继承GenericAPIView和对应的MIxin就好了。如下:

class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)


class BookDetailView(GenericAPIView, ListModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()
    lookup_field = 'id'

    def get(self, request, id):
        return self.retrieve(request, id)

    def put(self, request, id):
        return self.update(request, id)

    def delete(self, request, id):
        return self.destroy(request, id)

随便查看一个Mixin类,譬如这里查看全部对应的ListModelMixin类,可以看到其逻辑和我们原来写的Get方法的逻辑几乎一致。

class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

Mixin和GenericAPIView封装

注意此时各个方法的代码也固定了。所以DRF又做了一层封装。将GenericAPIView, ListModelMixin, CreateModelMixin和对应的

def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)

封装到了一起,成为ListCreateAPIView
同理GenericAPIView, ListModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin和对应的

def get(self, request, id):
        return self.retrieve(request, id)

    def put(self, request, id):
        return self.update(request, id)

    def delete(self, request, id):
        return self.destroy(request, id)

也封装到了一起,叫RetrieveUpdateDestroyAPIView。

所以最后整个视图代码如下:

class BookView(ListCreateAPIView):
    serializer_class = BookSerializer
    queryset = Book.objects.all()


class BookDetailView(RetrieveUpdateDestroyAPIView):
    serializer_class = BookSerializer
    queryset = Book.objects.all()
    lookup_field = 'id'

那么有些时候我只需要GenericAPIView, ListModelMixin, RetrieveModelMixin, UpdateModelMixin和对应方法的组合怎么办?也有对应封装的MixinAPIView类。

ViewSet

现在以上的代码已经很简洁很好了,唯一一点不足就是,分发机制不够好,导致必须有两个url,两个针对同一资源的视图类。而且请求方法固定了,一定要是get、post、put、delete等。此时ViewSet出现了。

我们重新写一下视图类代码,就拿最典型的获取所有资源和单一资源为例:

class BookView(ViewSet):
    def get_all(self, request):
        return Response("查看所有资源")

    def get_one(self, request, pk):
        return Response("查看单一资源")
    path('sers/book/', views.BookView.as_view({"get": "get_all"})),
    path('sers/book/<int:pk>/', views.BookView.as_view({"get": "get_one"})),

可以看ViewSet的源码:

class ViewSet(ViewSetMixin, views.APIView):
    """
    The base ViewSet class does not provide any actions by default.
    """
    pass

class ViewSetMixin:
    """
    This is the magic.

    Overrides `.as_view()` so that it takes an `actions` keyword that performs
    the binding of HTTP methods to actions on the Resource.

    For example, to create a concrete view binding the 'GET' and 'POST' methods
    to the 'list' and 'create' actions...

    view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
    """

    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        # actions must not be empty
        if not actions:
            raise TypeError("The `actions` argument must be provided when "
                            "calling `.as_view()` on a ViewSet. For example "
                            "`.as_view({'get': 'list'})`")

        # sanitize keyword arguments
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r" % (
                    cls.__name__, key))

        # name and suffix are mutually exclusive
        if 'name' in initkwargs and 'suffix' in initkwargs:
            raise TypeError("%s() received both `name` and `suffix`, which are "
                            "mutually exclusive arguments." % (cls.__name__))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)

            if 'get' in actions and 'head' not in actions:
                actions['head'] = actions['get']

            # We also store the mapping of request methods to actions,
            # so that we can later set the action attribute.
            # eg. `self.action = 'list'` on an incoming GET request.
            self.action_map = actions

            # Bind methods to actions
            # This is the bit that's different to a standard view
            for method, action in actions.items():
                handler = getattr(self, action)
                setattr(self, method, handler)

            self.request = request
            self.args = args
            self.kwargs = kwargs

            # And continue as usual
            return self.dispatch(request, *args, **kwargs)

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())

        # We need to set these on the view function, so that breadcrumb
        # generation can pick out these bits of information from a
        # resolved URL.
        view.cls = cls
        view.initkwargs = initkwargs
        view.actions = actions
        return csrf_exempt(view)

最核心的代码就是:

            # Bind methods to actions
            # This is the bit that's different to a standard view
            for method, action in actions.items():
                handler = getattr(self, action)
                setattr(self, method, handler)

相当于写了个拦截器,把方法get对应的函数变量从get()换为了urls中“get”对应的值的实例化变量,这里就是get_all()或者get_one()
意味着后续用getattr(self,method)时拿到的不是get()而是get_all()或者get_one()

也就是说ViewSet其实没有重写dispatch,而是在前面多加了个拦截器。
但是此时问题又来了,这个ViewSet没有之前Mixin和GenericAPIView的功能了。

GenericViewSet

所以GenericViewSet出现了。代码如下:

class BookView(GenericViewSet, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

urls.py

	path('sers/book/', views.BookView.as_view({"get": "list", "post": "create"})),
    path('sers/book/<int:pk>/', views.BookView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),

ModelViewSet

此时还有个封装:

GenericViewSet, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin

都封装成一个ModelViewSet。

class BookView(ModelViewSet):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

路由组件

然后还发现存在最后一个问题,就是ModelViewSet对应的路由还是很长很复杂,而且很固定。
此时路由组件router出现了。

router = routers.DefaultRouter()
router.register("sers/book", views.BookView)

urlpatterns = [
    path('admin/', admin.site.urls),
]

urlpatterns += router.urls

至此,drf视图相关的演化完毕。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1629183.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【知识】pycolmap.Sift.extract的参数和返回格式

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;请不吝给个[点赞、收藏、关注]哦~ import pycolmap# 比较推荐的参数 options {"peak_threshold": 0.0066667,"edge_threshold": 10,"first_octave"…

基于Spring Boot的火车订票管理系统设计与实现

基于Spring Boot的火车订票管理系统设计与实现 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 前台首页功能界面图&#xff0c;在系统首页可以查看…

python 函数作业 计算三角形的有效值s

题目&#xff1a; 计算半周长p&#xff0c;既p(abc)/2。 将p的值代入公式&#xff1a;计算出三角形的有效值。&#xff08;只能用python函数的方法求解&#xff09; 第一步先定义个函数&#xff1a; def isValid(side1,side2,side3) 第二步用if判断是否符合三角形边长准…

芯片尺寸封装(CSP)/晶圆级封装(WLP)/芯片尺寸晶圆级封装(CSWLP)

芯片尺寸封装&#xff08;CSP&#xff09;、晶圆级封装&#xff08;WLP&#xff09;、晶圆级芯片尺寸封装&#xff08;WLCSP&#xff09; 1.芯片尺寸封装&#xff08;CSP&#xff09;的定义是其尺寸不超过裸片尺寸的1.1倍。 2.晶圆级封装&#xff08;WLP&#xff09;是在晶圆…

AIGC——什么是人工智能生成内容

人工智能生成内容&#xff08;AIGC&#xff09;是当今数字时代的一个引人注目的前沿技术&#xff0c;它借助深度学习和自然语言处理等技术&#xff0c;使计算机系统具备了生成高质量文本、图像、音频等多媒体内容的能力。AIGC的出现不仅推动了信息技术的发展&#xff0c;也在多…

Linux搭建本地DNS服务器

目录 DNS进行域名解析的过程&#xff1a; 环境介绍&#xff1a; 环境准备&#xff1a; 1.安装bind 2.编辑主配 3.配置正向解析文件 4.测试&#xff1a; DNS进行域名解析的过程&#xff1a; 用户要访问www.baidu.com&#xff0c;会先找本机的host文件&#xff0c;再找本…

selenium 自动化测试课上实操指南2——乐视tv搜索

如果完成了实操1的同学&#xff0c;环境搭建已经ok&#xff0c;环境还没有好的同学请参考 实操1_百度搜索 为了大家顺利&#xff0c;我们还想按照实操1那样&#xff0c;先导入一个基本项目。在次基础上进行代码编写、 我们一起写写看。 1.打开乐视视频网页并最大化 如下图所…

深度学习从入门到精通——词向量介绍及应用

词向量介绍 词向量&#xff08;Word embedding&#xff09;&#xff0c;即把词语表示成实数向量。“好”的词向量能体现词语直接的相近关系。词向量已经被证明可以提高NLP任务的性能&#xff0c;例如语法分析和情感分析。词向量与词嵌入技术的提出是为了解决onehot的缺陷。它把…

【Yolov系列】Yolov5学习(一):大致框架

一、Yolov5网络结构 Yolov5特点&#xff1a; 合适于移动端部署&#xff0c;模型小&#xff0c;速度快 Yolov5骨干结构&#xff1a;CSPDarknet53网络Yolov5主要有Yolov5s、Yolov5m、Yolov5l、Yolov5x四个版本。这几个模型的结构基本一样&#xff0c;不同的是depth_multiple模型…

AUTOSAR-SD篇

1 概述 服务发现模块的主要任务是管理在车内通信中被称为服务的功能实体的可用性&#xff0c;以及控制事件消息的发送行为。只允许向需要这些事件消息的接收器发送事件消息&#xff08;发布/订阅&#xff09;。 这里描述的解决方案也被称为SOME/IP-SD&#xff08;基于IP -服务发…

西湖大学赵世钰老师【强化学习的数学原理】学习笔记-1、0节

强化学习的数学原理是由西湖大学赵世钰老师带来的关于RL理论方面的详细课程&#xff0c;本课程深入浅出地介绍了RL的基础原理&#xff0c;前置技能只需要基础的编程能力、概率论以及一部分的高等数学&#xff0c;你听完之后会在大脑里面清晰的勾勒出RL公式推导链条中的每一个部…

索引失效的几种场景

索引失效的几种场景 初始化数据一、对索引使用左或左右模糊匹配二、对索引使用函数三、对索引使用表达式计算四、对索引进行隐式类型转换五、索引使用不满足最左前缀原则六、where子句使用or总结 初始化数据 本文使用的是InnoDB存储引擎&#xff0c;先来创建一个学生表。 dro…

2021年山东省职业院校技能大赛高职组“信息安全管理与评估”样题

培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;624032112 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 移动应用开发群&#xff1a;548238632 极安云科专注于技能提升&#xff0c;赋能 2024年广东省高校的技…

【第二十五课】动态规划:数字三角形(acwing-898 / 蓝桥官网503 / c++代码)

目录 acwing-898数字三角形(模板题) 思路 注意点 代码 视频讲解推荐 2020蓝桥杯省赛-数字三角形 错误思路 (可不看) 思路 代码 注意点 续上之前的啦。 【第二十五课】动态规划&#xff1a;01背包问题(acwing-2 / 思路 / 含一维数组优化 / c代码) 适合在学习过背包…

OS复习笔记ch3-1

引言 学到第三章&#xff0c;就正式步入我们OS的大门了 本章我们将围绕以下几个问题去解决 什么是进程&#xff1f;进程状态有哪些&#xff1f;进程如何描述&#xff1f;进程如何控制&#xff1f; 本节内容主要是回答前两个问题&#xff0c;第二节回答后两个问题。 进程 …

windows环境下安装Apache

首先apache官网下载地址&#xff1a;http://www.apachelounge.com/download/按照自己的电脑操作系统来安装 这里我安装的是win64 主版本是2.4的apache。 然后解压压缩包到一个全英文的路径下&#xff01;&#xff01;&#xff01;一定一定不要有中文 中文符号也不要有&#xff…

详细分析PyInstaller打包python为exe执行文件(附Demo)

目录 前言1. 基本知识2. Demo 前言 需要将python文件打包成exe文件&#xff0c;变成rpa自动化形式 1. 基本知识 PyInstaller是一个用于将Python应用程序打包成独立可执行文件的工具 可以将Python脚本打包成Windows、Linux和Mac OS X上的可执行文件&#xff0c;这个作用可以将…

【Linux】软硬链接与动静态库(理解软硬链接的特点及使用场景、如何建立动静态库与使用第三方库)

一、软链接 1.1 如何建立软链接 //建立软链接 -s代表soft ln -s 目标文件名 链接文件名//删除软链接 rm 链接文件 或 unlink 链接文件 1.2 软链接的特点与功能 通过ls -i指令可以查看文件的inode编号 、 可以看出目标文件与软链接文件各自有自己的inode&#xff0c;所以软…

揭秘App全渠道统计服务:如何精准追踪你的用户来源?

在移动互联网时代&#xff0c;App的推广和运营至关重要&#xff0c;而渠道统计则是衡量推广效果的关键一环。近日&#xff0c;xinstall推出了一款全新的App全渠道统计服务&#xff0c;该服务旨在帮助开发者和运营者更全面地了解用户来源&#xff0c;优化推广策略&#xff0c;从…

前端JS加密库CryptoJS的常用方法

CryptoJS是前端常用的一个加密库&#xff0c;如MD5、SHA256、AES等加密算法。 官方文档&#xff1a;https://www.npmjs.com/package/crypto-js 安装方法 方法一&#xff1a;直接在html文件中引入 <script type"text/javascript" src"path-to/bower_componen…