• Django REST framework+Vue 打造生鲜超市(四)


    五、商品列表页

    5.1.django的view实现商品列表页

    (1)goods/view_base.py

    在goods文件夹下面新建view_base.py,为了区分django和django rest framework的view

    利用Django的view实现返回json数据

    复制代码
    # goods/view_base.py
    
    from django.views.generic import View
    from goods.models import Goods
    
    class GoodsListView(View):
        def get(self,request):
            #通过django的view实现商品列表页
            json_list = []
            #获取所有商品
            goods = Goods.objects.all()
            for good in goods:
                json_dict = {}
                #获取商品的每个字段,键值对形式
                json_dict['name'] = good.name
                json_dict['category'] = good.category.name
                json_dict['market_price'] = good.market_price
                json_list.append(json_dict)
    
            from django.http import HttpResponse
            import json
            #返回json,一定要指定类型content_type='application/json'
            return HttpResponse(json.dumps(json_list),content_type='application/json')
    复制代码

    (2)MxShop/urls.py

    复制代码
    from goods.view_base import GoodsListView
    
    urlpatterns = [
       #商品列表页
        path('goods/',GoodsListView.as_view(),name='goods-list')
    ]
    复制代码

    访问http://127.0.0.1:8000/goods/  可以获取商品列表信息的json数据

    5.2.django的serializer序列化model

    (1)model_to_dict

    当字段比较多时,一个字段一个字段的提取很麻烦,可以用model_to_dict,将model整个转化为dict

    复制代码
    # goods/view_base.py
    
    from django.views.generic import View
    from goods.models import Goods
    
    class GoodsListView(View):
        def get(self,request):
            #通过django的view实现商品列表页
            json_list = []
            #获取所有商品
            goods = Goods.objects.all()
            # for good in goods:
            #     json_dict = {}
            #     #获取商品的每个字段,键值对形式
            #     json_dict['name'] = good.name
            #     json_dict['category'] = good.category.name
            #     json_dict['market_price'] = good.market_price
            #     json_list.append(json_dict)
    
            from django.forms.models import model_to_dict
            for good in goods:
                json_dict = model_to_dict(good)
                json_list.append(json_dict)
    
            from django.http import HttpResponse
            import json
            #返回json,一定要指定类型content_type='application/json'
            return HttpResponse(json.dumps(json_list),content_type='application/json')
    复制代码

    但是这样有个问题,就是ImageFieldFile 和add_time字段不能序列化

     如何才能将所有字段序列化呢?就要用到django的serializers

    (2)django serializer的用法

    复制代码
    # goods/view_base.py
    
    from django.views.generic import View
    from goods.models import Goods
    
    class GoodsListView(View):
        def get(self,request):
            #通过django的view实现商品列表页
            json_list = []
            #获取所有商品
            goods = Goods.objects.all()
            # for good in goods:
            #     json_dict = {}
            #     #获取商品的每个字段,键值对形式
            #     json_dict['name'] = good.name
            #     json_dict['category'] = good.category.name
            #     json_dict['market_price'] = good.market_price
            #     json_list.append(json_dict)
    
            import json
            from django.core import serializers
            from django.http import JsonResponse
    
            json_data = serializers.serialize('json',goods)
            json_data = json.loads(json_data)
            #In order to allow non-dict objects to be serialized set the safe parameter to False.
            return JsonResponse(json_data,safe=False)
    复制代码

    django的serializer虽然可以很简单实现序列化,但是有几个缺点

    • 字段序列化定死的,要想重组的话非常麻烦
    • 从上面截图可以看出来,images保存的是一个相对路径,我们还需要补全路径,而这些drf都可以帮助我们做到

    以上写了这么多只是为了引入django rest framework和简单介绍django的序列化用法,下面就是重点讲解django rest framework了

    5.3.APIview方式实现商品列表页

    (1)安装

    • pip install coreapi                         drf的文档支持
    • pip install django-guardian           drf对象级别的权限支持

    (2)配置def文档的url

    MxShop/urls.py

    复制代码
    from rest_framework.documentation import include_docs_urls
    
    urlpatterns = [
        #drf文档,title自定义
        path('docs',include_docs_urls(title='仙剑奇侠传')),
    ]
    复制代码

     (3)配置rest_framework

    settings.py中添加

    INSTALLED_APPS = [
        'rest_framework',
    ]

    MxShop/urls.py

    urlpatterns = [
        path('api-auth/',include('rest_framework.urls')),
    ]

    (4)goods文件夹下面新建serializers.py

    用drf的序列化实现商品列表页展示,代码如下:

    复制代码
    # goods/serializers.py
    
    from rest_framework import serializers
    
    
    class GoodsSerializer(serializers.Serializer):
        name = serializers.CharField(required=True,max_length=100)
        click_num = serializers.IntegerField(default=0)
        goods_front_image = serializers.ImageField()
    复制代码

    (5)goods/views.py

    复制代码
    # googd/views.py
    
    from rest_framework.views import APIView
    from goods.serializers import GoodsSerializer
    from .models import Goods
    from rest_framework.response import Response
    
    
    class GoodsListView(APIView):
        '''
        商品列表
        '''
        def get(self,request,format=None):
            goods = Goods.objects.all()
            goods_serialzer = GoodsSerializer(goods,many=True)
            return Response(goods_serialzer.data)
    复制代码

    5.4.drf的Modelserializer实现商品列表页

    上面是用Serializer实现的,需要自己手动添加字段,如果用Modelserializer,会更加的方便,直接用__all__就可以全部序列化

    复制代码
    # goods/serializers.py
    
    from rest_framework import serializers
    from .models import Goods
    
    #Serializer实现商品列表页
    # class GoodsSerializer(serializers.Serializer):
    #     name = serializers.CharField(required=True,max_length=100)
    #     click_num = serializers.IntegerField(default=0)
    #     goods_front_image = serializers.ImageField()
    
    #ModelSerializer实现商品列表页
    class GoodsSerializer(serializers.ModelSerializer):
        class Meta:
            model = Goods
            fields = '__all__'
    复制代码

     category只显示分类的id,Serialzer还可以嵌套使用,覆盖外键字段

    复制代码
    # goods/serializers.py
    
    from rest_framework import serializers
    from .models import Goods,GoodsCategory
    
    #Serializer实现商品列表页
    # class GoodsSerializer(serializers.Serializer):
    #     name = serializers.CharField(required=True,max_length=100)
    #     click_num = serializers.IntegerField(default=0)
    #     goods_front_image = serializers.ImageField()
    
    
    class CategorySerializer(serializers.ModelSerializer):
        class Meta:
            model = GoodsCategory
            fields = "__all__"
    
    
    #ModelSerializer实现商品列表页
    class GoodsSerializer(serializers.ModelSerializer):
        #覆盖外键字段
        category = CategorySerializer()
        class Meta:
            model = Goods
            fields = '__all__'
    复制代码

    5.5.GenericView实现商品列表页

    (1)mixins和generic一起用用

    GenericAPIView继承APIView,封装了很多方法,比APIView功能更强大

     GenericAPIView源码
    用的时候需要定义queryset和serializer_class
    GenericAPIView里面默认为空
    • queryset = None
    • serializer_class = None

    ListModelMixin里面list方法帮我们做好了分页和序列化的工作,只要调用就好了

     ListModelMixin源码

    实现如下:

    复制代码
    from goods.serializers import GoodsSerializer
    from .models import Goods
    from rest_framework.response import Response
    from rest_framework import mixins
    from rest_framework import generics
    
    
    class GoodsListView(mixins.ListModelMixin,generics.GenericAPIView):
        '商品列表页'
        queryset = Goods.objects.all()
        serializer_class = GoodsSerializer
    
        def get(self,request,*args,**kwargs):
            return self.list(request,*args,**kwargs)
    复制代码

    上面的代码优化,可以直接继承ListAPIView,ListAPIView主要做了两件事:

    • ListAPIView(mixins.ListModelMixin,GenericAPIView)        继承了这两个类
    • 写好了get方法

     我们要获取商品列表页的信息,只要写三行代码就可以了

    class GoodsListView(generics.ListAPIView):
        '商品列表页'
        queryset = Goods.objects.all()
        serializer_class = GoodsSerializer

    5.6.添加分页功能

    先看rest_framework/settings.py源码,里面可以找到如何配置:比如认证、权限和分页等等

     rest_framework/settings源码

     添加分页功能,配置如下:

    复制代码
    REST_FRAMEWORK = {
        #分页
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
        #每页显示的个数
        'PAGE_SIZE': 10,
    }
    复制代码

    自定也分页功能

    复制代码
    from rest_framework.pagination import PageNumberPagination
    
    class GoodsPagination(PageNumberPagination):
        '''
        商品列表自定义分页
        '''
        #默认每页显示的个数
        page_size = 10
        #可以动态改变每页显示的个数
        page_size_query_param = 'page_size'
        #页码参数
        page_query_param = 'page'
        #最多能显示多少页
        max_page_size = 100
    
    
    class GoodsListView(generics.ListAPIView):
        '商品列表页'
    
        pagination_class = GoodsPagination    #分页
        queryset = Goods.objects.all()
        serializer_class = GoodsSerializer
    复制代码

    settings.py里面就不用设置了

    5.7.viewsets和router完成商品列表页

    主要用到viewsets中的GenericViewSet

    再看下ViewSerMixin源码

     ViewSetMixin源码

     ViewSets和Routers结合使用

     MxShop/yrls.py

    复制代码
    from goods.views import GoodsListViewSet
    from rest_framework.routers import DefaultRouter
    
    router = DefaultRouter()
    
    #配置goods的url
    router.register(r'goods', GoodsListViewSet)
    
    urlpatterns = [
        #商品列表页
        re_path('^', include(router.urls)),
    ]
    复制代码

    views.py

    必须定义一个默认的排序方式

    复制代码
    class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
        '商品列表页'
    
        # 分页
        pagination_class = GoodsPagination
        #这里必须要定义一个默认的排序,否则会报错
        queryset = Goods.objects.all().order_by('id')
        serializer_class = GoodsSerializer
    复制代码

    5.8.drf的APIView、GenericView、viewsets和router的原理分析

    genericViewSet 是最高的一层

    往下

    GenericViewSet(viewsets)     ----drf

      GenericAPIView                  ---drf

        APIView                        ---drf

          View            ----django

    这些view功能的不同,主要的是有mixin的存在

    mixins总共有五种:

      CreateModelMixin

      ListModelMixin

      UpdateModelMixin

      RetrieveModelMixin

      DestoryModelMixin

     mixins.py源码

    以ListModelMixin为例:

    如果不继承ListModelMixin的话,就无法将get和商品的列表关联起来,另外还有其中的分页等等,都无法实现。

    还有其它几个mixin(增删改查局部),这些功能都是mixin做的

     我们一般都是用viewsets

    ViewSet类与View类其实几乎是相同的,但提供的是read或update这些操作,而不是get或put 等HTTP动作。同时,ViewSet为我们提供了默认的URL结构, 使得我们能更专注于API本身。

     Router提供了一种简单,快速,集成的方式来定义一系列的urls

     5.9.drf的request和response介绍

    REST framework 的 Request 类扩展与标准的 HttpRequest,并做了相应的增强,比如更加灵活的请求解析(request parsing)和认证(request authentication)。

    Request 解析

    REST framwork 的 Request 对象提供了灵活的请求解析,允许你使用 JSON data 或 其他 media types 像通常处理表单数据一样处理请求。

    .data

    request.data 返回请求主题的解析内容。这跟标准的 request.POST 和 request.FILES 类似,并且还具有以下特点:

    • 包括所有解析的内容,文件(file) 和 非文件(non-file inputs)。
    • 支持解析 POST 以外的 HTTP method , 比如 PUT, PATCH
    • 更加灵活,不仅仅支持表单数据,传入同样的 JSON 数据一样可以正确解析,并且不用做额外的处理(意思是前端不管提交的是表单数据,还是 JSON 数据,.data 都能够正确解析)。

    .data 具体操作,以后再说~

    .query_params

    request.query_params 等同于 request.GET,不过其名字更加容易理解。

    为了代码更加清晰可读,推荐使用 request.query_params ,而不是 Django 中的 request.GET,这样那够让你的代码更加明显的体现出 ----- 任何 HTTP method 类型都可能包含查询参数(query parameters),而不仅仅只是 'GET' 请求。

    .parser

    APIView 类或者 @api_view 装饰器将根据视图上设置的 parser_classes 或 settings 文件中的 DEFAULT_PARSER_CLASSES 设置来确保此属性(.parsers)自动设置为 Parser 实例列表。

    通常不需要关注该属性......

    如果你非要看看它里面是什么,可以打印出来看看,大概长这样:

    [<rest_framework.parsers.JSONParser object at 0x7fa850202d68>, <rest_framework.parsers.FormParser object at 0x7fa850202be0>, <rest_framework.parsers.MultiPartParser object at 0x7fa850202860>]

    包含三个解析器 JSONParserFormParserMultiPartParser

    注意: 如果客户端发送格式错误的内容,则访问 request.data 可能会引发 ParseError 。默认情况下, REST framework 的 APIView 类或者 @api_view 装饰器将捕获错误并返回 400 Bad Request 响应。 如果客户端发送的请求内容无法解析(不同于格式错误),则会引发 UnsupportedMediaType 异常,默认情况下会被捕获并返回 415 Unsupported Media Type 响应。

    Responses

    与基本的 HttpResponse 对象不同,TemplateResponse 对象保留了视图提供的用于计算响应的上下文的详细信息。直到需要时才会计算最终的响应输出,也就是在后面的响应过程中进行计算。 — Django 文档

    REST framework 通过提供一个 Response 类来支持 HTTP 内容协商,该类允许你根据客户端请求返回不同的表现形式(如: JSON ,HTML 等)。

    Response 类的子类是 Django 的 SimpleTemplateResponseResponse 对象使用数据进行初始化,数据应由 Python 对象(native Python primitives)组成。然后 REST framework 使用标准的 HTTP 内容协商来确定它应该如何渲染最终响应的内容。

    当然,您也可以不使用 Response 类,直接返回常规 HttpResponse 或 StreamingHttpResponse 对象。 使用 Response 类只是提供了一个更好的交互方式,它可以返回多种格式。

    除非由于某种原因需要大幅度定制 REST framework ,否则应该始终对返回 Response 对象的视图使用 APIView 类或 @api_view 装饰器。这样做可以确保视图执行内容协商,并在视图返回之前为响应选择适当的渲染器。

    创建 response

    Response()

    与普通 HttpResponse 对象不同,您不会使用渲染的内容实例化 Response 对象。相反,您传递的是未渲染的数据,可能包含任何 Python 对象。

    由于 Response 类使用的渲染器不能处理复杂的数据类型(比如 Django 的模型实例),所以需要在创建 Response 对象之前将数据序列化为基本的数据类型。

    你可以使用 REST framework 的 Serializer 类来执行序列化的操作,也可以用自己的方式来序列化。

    构造方法: Response(data, status=None, template_name=None, headers=None, content_type=None)

    参数:

    • data: 响应的序列化数据。
    • status: 响应的状态代码。默认为200。
    • template_name: 选择 HTMLRenderer 时使用的模板名称。
    • headers: 设置 HTTP header,字典类型。
    • content_type: 响应的内容类型,通常渲染器会根据内容协商的结果自动设置,但有些时候需要手动指定。

    属性

    .data

    还没有渲染,但已经序列化的响应数据。

    .status_code

    状态码

    .content

    将会返回的响应内容,必须先调用 .render() 方法,才能访问 .content 。

    .template_name

    只有在 response 的渲染器是 HTMLRenderer 或其他自定义模板渲染器时才需要提供。

    .accepted_renderer

    用于将会返回的响应内容的渲染器实例。

    从视图返回响应之前由 APIView 或 @api_view 自动设置。

    .accepted_media_type

    内容协商阶段选择的媒体类型。

    从视图返回响应之前由 APIView 或 @api_view 自动设置。

    .renderer_context

    将传递给渲染器的 .render() 方法的附加的上下文信息字典。

    从视图返回响应之前由 APIView 或 @api_view 自动设置。

    标准 HttpResponse 属性

    Response 类扩展于 SimpleTemplateResponse,并且响应中也提供了所有常用的属性和方法。例如,您可以用标准方式在响应中设置 header:

    response = Response()
    response['Cache-Control'] = 'no-cache'

    .render()

    与其他任何 TemplateResponse 一样,调用此方法将响应的序列化数据呈现为最终响应内容。响应内容将设置为在 accepted_renderer 实例上调用 .render(data,accepted_media_type,renderer_context) 方法的结果。

    通常不需要自己调用 .render() ,因为它是由 Django 处理的。

    5.10.drf的过滤

    drf的filter用法   http://www.django-rest-framework.org/api-guide/filtering/

    (1)添加到app里面

    INSTALLED_APPS = [
         'django_filters',
    ]

    (2)新建filter.py

    自定义一个过滤器

    复制代码
    # goods/filters.py
    
    import django_filters
    
    from .models import Goods
    
    
    class GoodsFilter(django_filters.rest_framework.FilterSet):
        '''
        商品过滤的类
        '''
        #两个参数,name是要过滤的字段,lookup是执行的行为,‘小与等于本店价格’
        price_min = django_filters.NumberFilter(name="shop_price", lookup_expr='gte')
        price_max = django_filters.NumberFilter(name="shop_price", lookup_expr='lte')
    
        class Meta:
            model = Goods
            fields = ['price_min', 'price_max']
    复制代码

    (3)views.py

    复制代码
    
    
    from .filters import GoodsFilter
    from django_filters.rest_framework import DjangoFilterBackend

    class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet): '商品列表页' #这里必须要定义一个默认的排序,否则会报错 queryset = Goods.objects.all().order_by('id') # 分页 pagination_class = GoodsPagination serializer_class = GoodsSerializer filter_backends = (DjangoFilterBackend,) # 设置filter的类为我们自定义的类 filter_class = GoodsFilter
    复制代码

    5.11.drf的搜索和排序

     添加搜索功能

    搜索的字段可以使用正则表达式,更加的灵活

    复制代码
    class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
        '商品列表页'
    
        #这里必须要定义一个默认的排序,否则会报错
        queryset = Goods.objects.all().order_by('id')
        # 分页
        pagination_class = GoodsPagination
        serializer_class = GoodsSerializer
        filter_backends = (DjangoFilterBackend,filters.SearchFilter)
    
        # 设置filter的类为我们自定义的类
        filter_class = GoodsFilter
        #搜索,=name表示精确搜索,也可以使用各种正则表达式
        search_fields = ('=name','goods_brief')
    复制代码

     添加排序功能

    复制代码
    class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
        '商品列表页'
    
        #这里必须要定义一个默认的排序,否则会报错
        queryset = Goods.objects.all()
        # 分页
        pagination_class = GoodsPagination
        #序列化
        serializer_class = GoodsSerializer
        filter_backends = (DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter)
    
        # 设置filter的类为我们自定义的类
        #过滤
        filter_class = GoodsFilter
        #搜索,=name表示精确搜索,也可以使用各种正则表达式
        search_fields = ('=name','goods_brief')
        #排序
        ordering_fields = ('sold_num', 'add_time')
    复制代码

    所有代码

    # googd/views.py

    from rest_framework.views import APIView
    from goods.serializers import GoodsSerializer
    from .models import Goods
    from rest_framework.response import Response
    from rest_framework import mixins
    from rest_framework import generics
    from rest_framework.pagination import PageNumberPagination
    from rest_framework import viewsets
    from .filters import GoodsFilter
    from django_filters.rest_framework import DjangoFilterBackend
    from rest_framework import filters


    class GoodsPagination(PageNumberPagination):
    '''
    商品列表自定义分页
    '''
    #默认每页显示的个数
    page_size = 10
    #可以动态改变每页显示的个数
    page_size_query_param = 'page_size'
    #页码参数
    page_query_param = 'page'
    #最多能显示多少页
    max_page_size = 100


    class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
    '商品列表页'

    #这里必须要定义一个默认的排序,否则会报错
    queryset = Goods.objects.all()
    # 分页
    pagination_class = GoodsPagination
    #序列化
    serializer_class = GoodsSerializer
    filter_backends = (DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter)

    # 设置filter的类为我们自定义的类
    #过滤
    filter_class = GoodsFilter
    #搜索,=name表示精确搜索,也可以使用各种正则表达式
    search_fields = ('=name','goods_brief')
    #排序
    ordering_fields = ('sold_num', 'add_time')

    views.py

    复制代码
    # googd/views.py
    
    from rest_framework.views import APIView
    from goods.serializers import GoodsSerializer
    from .models import Goods
    from rest_framework.response import Response
    from rest_framework import mixins
    from rest_framework import generics
    from rest_framework.pagination import PageNumberPagination
    from rest_framework import viewsets
    from .filters import GoodsFilter
    from django_filters.rest_framework import DjangoFilterBackend
    from rest_framework import filters
    
    
    class GoodsPagination(PageNumberPagination):
        '''
        商品列表自定义分页
        '''
        #默认每页显示的个数
        page_size = 10
        #可以动态改变每页显示的个数
        page_size_query_param = 'page_size'
        #页码参数
        page_query_param = 'page'
        #最多能显示多少页
        max_page_size = 100
    
    
    class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
        '商品列表页'
    
        #这里必须要定义一个默认的排序,否则会报错
        queryset = Goods.objects.all()
        # 分页
        pagination_class = GoodsPagination
        #序列化
        serializer_class = GoodsSerializer
        filter_backends = (DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter)
    
        # 设置filter的类为我们自定义的类
        #过滤
        filter_class = GoodsFilter
        #搜索,=name表示精确搜索,也可以使用各种正则表达式
        search_fields = ('=name','goods_brief')
        #排序
        ordering_fields = ('sold_num', 'add_time')
    复制代码

    # googd/views.py

    from rest_framework.views import APIView
    from goods.serializers import GoodsSerializer
    from .models import Goods
    from rest_framework.response import Response
    from rest_framework import mixins
    from rest_framework import generics
    from rest_framework.pagination import PageNumberPagination
    from rest_framework import viewsets
    from .filters import GoodsFilter
    from django_filters.rest_framework import DjangoFilterBackend
    from rest_framework import filters


    class GoodsPagination(PageNumberPagination):
    '''
    商品列表自定义分页
    '''
    #默认每页显示的个数
    page_size = 10
    #可以动态改变每页显示的个数
    page_size_query_param = 'page_size'
    #页码参数
    page_query_param = 'page'
    #最多能显示多少页
    max_page_size = 100


    class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
    '商品列表页'

    #这里必须要定义一个默认的排序,否则会报错
    queryset = Goods.objects.all()
    # 分页
    pagination_class = GoodsPagination
    #序列化
    serializer_class = GoodsSerializer
    filter_backends = (DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter)

    # 设置filter的类为我们自定义的类
    #过滤
    filter_class = GoodsFilter
    #搜索,=name表示精确搜索,也可以使用各种正则表达式
    search_fields = ('=name','goods_brief')
    #排序
    ordering_fields = ('sold_num', 'add_time')

    views.py

    复制代码
    # MxShop/urls.py
    __author__ = 'derek'
    
    
    from django.urls import path,include,re_path
    import xadmin
    from django.views.static import serve
    from MxShop.settings import MEDIA_ROOT
    # from goods.view_base import GoodsListView
    
    from rest_framework.documentation import include_docs_urls
    from goods.views import GoodsListViewSet
    from rest_framework.routers import DefaultRouter
    
    router = DefaultRouter()
    
    #配置goods的url
    router.register(r'goods', GoodsListViewSet)
    
    urlpatterns = [
        path('xadmin/', xadmin.site.urls),
        path('api-auth/',include('rest_framework.urls')),
        path('ueditor/',include('DjangoUeditor.urls' )),
        #文件
        path('media/<path:path>',serve,{'document_root':MEDIA_ROOT}),
        #drf文档,title自定义
        path('docs',include_docs_urls(title='仙剑奇侠传')),
        #商品列表页
        re_path('^', include(router.urls)),
    ]
    复制代码
  • 相关阅读:
    图论知识补全
    字符串
    Yii2安装搭建和将入口文件移到根目录
    yii2史上最简单式安装教程,没有之一
    如何在IIS 7.5中部署Asp.Net MVC 5的网站
    Yii2.0中文开发向导——Yii2中多表关联查询(join、joinwith)
    Yii2 AR find用法 (2016-05-18 12:06:01)
    DedeCMS织梦动态分页类,datalist标签使用实例
    dedecms为后台自定义菜单的完整方法
    php和js一起实现倒计时功能
  • 原文地址:https://www.cnblogs.com/daluozi/p/9467337.html
Copyright © 2020-2023  润新知