• pyramid基本用法


    模板

    alchemy:基础功能加数据库--常用

    starter:只包含pyramid最基本功能,简单模板,无数据库

    zodb:

    创建项目

    pcreate -s alchemy 项目名称

     项目创建完成后进入到项目路径

    python setup.py develop  # 不会把代码复制过去,会在site-packages里创建一个路径,还是回去项目目录里找代码
    
    python setup.py install  # 把项目需要的依赖全部复制到site-packages里,就算是把项目目录删掉都可以用,会导致项目里改代码的话不生效

    查看MyProject.egg-link

    /home/python/.virtualenv/虚拟环境/lib/python2.7/site-packages

    这里有MyProject.egg-link和其它的一些依赖

    依赖包

     打开debug调试

    在development.ini中打开

    debugtoolbar.hosts = 192.168.2.3  # 输入想在哪个ip调试

     development.ini详细介绍

    ###
    # app configuration
    # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
    ###
    
    [app:main]  # 一个中括号是一个节
    use = egg:MyProject  # egg后面跟项目名称
    
    pyramid.reload_templates = true  # 对模板进行修改后是否自动重载,开发阶段可以用来调试,正式环境会降低渲染速度 
    pyramid.debug_authorization = false  # 认证和权限相关的调试信息
    pyramid.debug_notfound = false  # 找不到页面的时候提示相关信息
    pyramid.debug_routematch = false  # url映射机制调试
    pyramid.default_locale_name = en  # 程序默认语言
    pyramid.includes =        # 可以多条 加载pyramid相关插件
        pyramid_debugtoolbar
        pyramid_tm
    
    sqlalchemy.url = sqlite:///%(here)s/devdata.db
    # sqlalchemy.url = mysql://root:mysql@localhost:3306/pyramid_myproject?charset=utf8
    # By default, the toolbar only appears for clients from IP addresses
    # '127.0.0.1' and '::1'.
    # debugtoolbar.hosts = 127.0.0.1 ::1  # 调试工具条 只能在本地使用
    
    ###
    # wsgi server configuration
    ###
    
    [server:main]
    use = egg:waitress#main
    host = 192.168.230.128  # 监听ip
    port = 5678  # 端口
    
    ###
    # logging configuration
    # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
    ###
    
    [loggers]
    keys = root, myproject, sqlalchemy
    
    [handlers]
    keys = console
    
    [formatters]
    keys = generic
    
    [logger_root]
    level = INFO
    handlers = console
    
    [logger_myproject]
    level = DEBUG
    handlers =
    qualname = myproject
    
    [logger_sqlalchemy]
    level = INFO
    handlers =
    qualname = sqlalchemy.engine
    # "level = INFO" logs SQL queries.
    # "level = DEBUG" logs SQL queries and results.
    # "level = WARN" logs neither.  (Recommended for production systems.)
    
    [handler_console]
    class = StreamHandler
    args = (sys.stderr,)
    level = NOTSET
    formatter = generic
    
    [formatter_generic]
    format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s

     初始化数据

    initialize_[项目名称]_db development.ini

    存储模型设计

    model.py

    class Users(Base):
        __tablename__ = 'users'
        id = Column(Integer, primary_key=True)  # Column固定用法
        name = Column(Unicode(255), unique=True)
        password = Column(Unicode(255))
        email = Column(Unicode(255), unique=True)
        group_id = Column(Integer)
    
        def __init__(self, name, password, email, group_id):
            self.name = name
            self.password = password
            self.email = email
            self.group_id = group_id

     注册到initializedb.py中

    import os
    import sys
    import transaction
    
    from sqlalchemy import engine_from_config
    
    from pyramid.paster import (
        get_appsettings,
        setup_logging,
        )
    
    from ..models import (
        DBSession,
        MyModel,
        Users,
        Base,
        )
    
    
    def usage(argv):
        cmd = os.path.basename(argv[0])
        print('usage: %s <config_uri>
    '
              '(example: "%s development.ini")' % (cmd, cmd))
        sys.exit(1)
    
    
    def main(argv=sys.argv):
        if len(argv) != 2:
            usage(argv)
        config_uri = argv[1]
        setup_logging(config_uri)
        settings = get_appsettings(config_uri)
        engine = engine_from_config(settings, 'sqlalchemy.')
        DBSession.configure(bind=engine)
        Base.metadata.create_all(engine)
        with transaction.manager:
            # model = MyModel(name='one', value=1)
            # DBSession.add(model)
            admin = Users()
            admin.name = 'admin'
            admin.password = 'admin'
            admin.email = '88983860@qq.com'
            DBSession.add(admin)

    删除原来的devdata.db,重新初始化数据库

     数据存储类型设计

    多对多,多表关联

    自关联

    # -*- coding:UTF-8 -*-
    from datetime import datetime
    from sqlalchemy import (
        Table,          # 创建关联表的时候要导入
        Column,
        ForeignKey,     # 引用外键
        Integer,        # 整型
        Text,           # 文本类型
        Unicode,        # Unicode类型
        DateTime,       # 时间类型
        Float,          # 浮点型
        )
    
    from sqlalchemy.ext.declarative import declarative_base
    
    from sqlalchemy.orm import (
        scoped_session,
        sessionmaker,
        relationship,  # 引用关联数据
        )
    
    from zope.sqlalchemy import ZopeTransactionExtension
    
    DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
    Base = declarative_base()
    
    
    class User(Base):
        __tablename__ = 'users'
        id = Column(Integer, primary_key=True)  # primary_key 主键
        name = Column(Unicode(255), nullable=False, unique=True)  # nullable=False
                                                                  # 不允许为空 unique 唯一
        password = Column(Unicode(255), nullable=False)
        email = Column(Unicode(255), unique=True)
        group_id = Column(Integer, ForeignKey('groups.id'), nullable=False)  # 关联外键groups的id
        group = relationship('Group', backref='users')
        # 可以使用User的实例对象+".group"查询Group的数据,
        #  backref可以使用Group的实例对象查询User的数据,
        # 以列表形式返回 相当于在Group里写users = relationship('User')
    
    # 创建关联中间表
    # 中间表 = Table(表名, Base.metadata,字段1,字段2....)
    # Base.metadata 固定用法
    group_permisson = Table('group_permission', Base.metadata,
        Column('group_id', Integer, ForeignKey('groups.id'), primary_key=True),  # 字段前要加字段名称
        Column('permission_id', Integer, ForeignKey('permissions.id'),
               primary_key=True)
                            )
    
    class Group(Base):
        __tablename__ = 'groups'
        id = Column(Integer, primary_key=True)
        name = Column(Unicode(255), nullable=False)
    
        permission = relationship('Permission', secondary='group_permission',  # 从表关联
                                  backref='groups')
    
    class Permission(Base):
        __tablename__ = 'permissions'
        id = Column(Integer, primary_key=True)
        name = Column(Unicode(255), nullable=False)
    
    class Item(Base):
        __tablename__ = 'items'
        id = Column(Integer, primary_key=True)
        name = Column(Unicode(255), nullable=False, unique=True)
        description = Column(Text)
        price = Column(Float, nullable=False, default=0.00)
    
        category_id = Column(Integer, ForeignKey('categories.id'), nullable=False)
        category = relationship('Category', backref='items')
    
    class Category(Base):
        __tablename__ = 'categories'
        id = Column(Integer, primary_key=True)
        name = Column(Unicode(255), nullable=False, unique=True)
        parent_id = Column(Integer, ForeignKey('categories.id'), nullable=True)
        parent = relationship('Category', remote_side=[id],   # 外键是自己的时候需要加入remote_side
                              backref='children')
    
    class ItemImage(Base):
        __tablename__ = 'images'
        id = Column(Integer, primary_key=True)
        path = Column(Unicode(255), nullable=False)
        item_id = Column(Integer, ForeignKey('items.id'), nullable=False)
        item = relationship('Item', backref='images')
    
    class Comment(Base):
        __tablename__ = 'comments'
        id = Column(Integer, primary_key=True)
        user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
        user = relationship('User', backref='comments')
        item_id = Column(Integer, ForeignKey('items.id'), nullable=False)
        item = relationship('Item', backref='comments')
    
        rank = Column(Integer, nullable=False, default=3)
        content = Column(Text)
    
    cart_item = Table('cart_item', Base.metadata,
        Column('cart_id', Integer, ForeignKey('carts.id'), primary_key=True),
        Column('item_id', Integer, ForeignKey('items.id'), primary_key=True)
                      )
    
    class Cart(Base):
        __tablename__ = 'carts'
        id = Column(Integer, primary_key=True)
    
        items = relationship('Item', secondary='cart_item')
        user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
        user = relationship('User', backref='cart')
    
    order_item = Table('order_item', Base.metadata,
        Column('order_id', Integer, ForeignKey('orders.id'), primary_key=True),
        Column('item_id', Integer, ForeignKey('items.id'), primary_key=True)
                   )
    
    class Order(Base):
        __tablename__ = 'orders'
        id = Column(Integer, primary_key=True)
        user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
        user = relationship('User')
    
        items = relationship('Item', secondary='order_item')
        add_time = Column(DateTime, nullable=False, default=datetime.now())
        address = Column(Unicode(255), nullable=False)
        telephone = Column(Unicode(25), nullable=False)

     配置路由

     __init__.py

    # -*- coding:UTF-8 -*-
    from pyramid.config import Configurator
    from sqlalchemy import engine_from_config
    
    from .models import (
        DBSession,
        Base,
        )
    
    
    def main(global_config, **settings):
        """ This function returns a Pyramid WSGI application.
        """
        engine = engine_from_config(settings, 'sqlalchemy.')
        DBSession.configure(bind=engine)
        Base.metadata.bind = engine
        config = Configurator(settings=settings)
        config.add_static_view('static', 'static', cache_max_age=3600)  # 静态资源
        config.add_route('home', '/')  # url映射 对应home主页
        config.add_route('category', '/category')  # url映射 对应category页面
        config.scan()
        return config.make_wsgi_app()

    视图函数views.py函数中

    # -*- coding:UTF-8 -*-
    from pyramid.response import Response
    from pyramid.view import view_config
    
    
    from .models import (
        DBSession,
        )
    
    
    @view_config(route_name='home', renderer='templates/mytemplate.pt')
    def my_view(request):
        # try:
        #     one = DBSession.query(MyModel).filter(MyModel.name == 'one').first()
        # except DBAPIError:
        #     return Response(conn_err_msg, content_type='text/plain', status_int=500)
        return {'one': 'one', 'project': '我是天才'}
    
    
    @view_config(route_name='category', renderer='string')  # route_name对应__init__.py中的config.add_route('category', '/category')
    def category_view(request):
        return 'This is category!'

    使用视图类view_defaults

    创建views文件夹

     创建base.py文件

    import logging
    from pyramid.httpexceptions import HTTPFound, HTTPBadRequest, HTTPServerError, 
        HTTPForbidden, HTTPUnauthorized
    import json
    
    log = logging.getLogger(__name__)
    
    
    class Base(object):
        def __init__(self, request):
            self.request = request
    
    
    class CBase(Base):
        def __init__(self, request):
            Base.__init__(self, request)

    创建视图控制器categories.py

    # -*- coding:UTF-8 -*-
    from pyramid.response import Response
    from pyramid.view import view_config, view_defaults
    from myshop.lib import category  # 引用category数据文件
    from base import CBase
    
    ctrl = 'categories'
    
    # @view_config(route_name='home', renderer='templates/mytemplate.pt')
    @view_defaults(route_name='/')  # url映射对应__init__.py中的config.add_route('/', '/{ctrl}/{action}')
    class categories(CBase):
        def __init__(self, request):
            CBase.__init__(self, request)
            self.request.title = '分类'
    
        @view_config(match_param=('ctrl=%s' % ctrl, 'action=view'),  # 对应控制器文件和方法
                     renderer="mytemplate.html")
        def view(request):
            category_list = category.get_category_list()
            return {'one': 'one', 'project': category_list}

    __init__.py文件中修改路由

    # -*- coding:UTF-8 -*-
    from pyramid.config import Configurator
    from sqlalchemy import engine_from_config
    
    from myshop.models import (
        DBSession,
        Base,
        )
    
    
    def main(global_config, **settings):
        """ This function returns a Pyramid WSGI application.
        """
        engine = engine_from_config(settings, 'sqlalchemy.')
        DBSession.configure(bind=engine)
        Base.metadata.bind = engine
        config = Configurator(settings=settings)
        config.add_renderer(".html", 'pyramid.mako_templating.renderer_factory')
        config.add_static_view('static', 'static', cache_max_age=3600)  # 静态资源
        # config.add_route('home', '/')  # url映射 对应home主页
        # config.add_route('category', '/category')  # url映射 对应category页面
        config.add_route('/', '/{ctrl}/{action}')  # url映射 对应category页面
        config.scan()
        return config.make_wsgi_app()
    带参数路由

    配置url映射

    config.add_route('/', '/{ctrl}/{action}/{id}')  # url映射 携带id参数

    视图函数views/item.py中接收参数

    # -*- coding:UTF-8 -*-
    from pyramid.response import Response
    from pyramid.view import view_config, view_defaults
    from myshop.lib import category
    from base import CBase
    
    ctrl = 'item'
    
    # @view_config(route_name='home', renderer='templates/mytemplate.pt')
    @view_defaults(route_name='/')
    class item(CBase):
        def __init__(self, request):
            CBase.__init__(self, request)
            self.request.title = '商品'
    
        @view_config(match_param=('ctrl=%s' % ctrl, 'action=view'),
                     renderer="item.html")
                     # renderer="string")
        def view(self):
            id = self.request.matchdict.get('id')  # 接收id
            result = {}
            result['id'] = id
            return result

    渲染到模板item.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>${id}</h1>
    </body>
    </html>

    模板引擎换成mako以及后缀换成'.html'

    1. 在配置文件development.ini中,添加上:

    mako.directories = [project name]:[root path]

    mako.directories = myshop:templates  # 更改为mako模板 mako.directories = [project name]:[root path] 项目名:html文件目录
    mako.strict_undefined = true

    project name是你项目的名称

    root path 是你模板文件存放的根目录

    跟多关于mako的设置: https://pyramid.readthedocs.io/en/1.3-branch/narr/environment.html#mako-template-render-settings

    2. 修改项目的__init__.py文件,在main函数中添加上:

    config.add_renderer('.html', 'pyramid.mako_templating.renderer_factory')

    凡是使用.html结尾的模板,都会使用mako引擎

    3. 当在View.py中,使用.html的模板,就会使用mako模板引擎了。

    @view_config(match_param=('ctrl=%s' % ctrl, 'action=view'),
                     renderer="mytemplate.html")
        def view(request):
            # try:
            #     one = DBSession.query(MyModel).filter(MyModel.name == 'one').first()
            # except DBAPIError:
            #     return Response(conn_err_msg, content_type='text/plain', status_int=500)
            return {'one': 'one', 'project': 'TTTTT'}

     出错

    File "/home/python/.virtualenvs/pyramid_py2/local/lib/python2.7/site-packages/mako/lookup.py", line 263, in get_template
        "Cant locate template for uri %r" % uri
    TopLevelLookupException: Cant locate template for uri 'mytemplate.html'

    development.int文件里mako配置的时候不能在后面加注释

    mako.directories = myshop:templates  # 更改为mako模板 mako.directories = [project name]:[root path] 项目名:html文件目录

    导致找不到模板

    # 更改为mako模板 mako.directories = [project name]:[root path] 项目名:html文件目录
    mako.directories = myshop:templates

    解决

    认证和权限

    配置__init__.py

    # -*- coding:UTF-8 -*-
    from pyramid.config import Configurator
    from pyramid.authentication import AuthTktAuthenticationPolicy  # 认证
    from pyramid.authorization import ACLAuthorizationPolicy        # 权限
    from pyramid.security import Allow
    from sqlalchemy import engine_from_config
    from myshop.lib import user
    from myshop.models import (
        DBSession,
        Base,
        )
    
    def groupfinder(userid, request):
        """每当用户登录的时候,每当认证机制检查用户是否存在时都会调用下这个函数"""
        print("*" * 100)
        user_info = user.get_user_by_id(userid)  # 查询登录账户信息
        if user_info:
            return [user.group.id]
        return None
    
    class RootFactory(object):   # 创建RootFactory类
        def __init__(self, request):
            """读取所有权限"""
            group_list = user.get_group_list()  # 查询所有的组
            self.__acl__ = []  # 准备acl列表
            for group in group_list:
                for permission in group.permissions:
                    # 给acl列表添加元祖,  1.Allow 允许或拒绝 需要导入Allow库 2.为了不和其它id冲突 添加g:的标识符 3.权限名称
                    self.__acl__.append(
                        (Allow, 'g:' + str(group.id), permission.name)
                    )
    
    
    def main(global_config, **settings):
        """ This function returns a Pyramid WSGI application.
        """
        engine = engine_from_config(settings, 'sqlalchemy.')
        DBSession.configure(bind=engine)
        Base.metadata.bind = engine
        # 权限和授权
        # 认证机制
         authn_policy = AuthTktAuthenticationPolicy(
                     'secret',  # 加密密钥
                     callback = groupfinder,  # 用于查询用户属于哪个用户组,以及这个用户是否存在
                     hashalg = 'sha512'  # hashalg算法,用什么方式进行加密
                        )
    
        # 授权机制
        authz_policy = ACLAuthorizationPolicy()
    
        # config = Configurator(settings=settings)
        # config配置里添加权限
        config = Configurator(settings=settings, root_factory='myshop.RootFactory')
        # 添加认证机制到config
        config.set_authentication_policy(authn_policy)
        # 添加授权机制
        config.set_authorization_policy(authz_policy)
    
        config.add_renderer(".html", 'pyramid.mako_templating.renderer_factory')
        config.add_static_view('static', 'static', cache_max_age=3600)  # 静态资源
        # config.add_route('home', '/')  # url映射 对应home主页
        # config.add_route('category', '/category')  # url映射 对应category页面
        config.add_route('/', '/{ctrl}/{action}*pa')  # url映射 对应控制器-方法 页面
        config.add_route('index', '/{action:.*}')  # url映射 对应控制器-方法 页面
        config.scan()
        return config.make_wsgi_app()

    RootFactory要做的事

    查出user.id ---> 通过user查出组 user.group.id -> 保存起来 save it

    查出组的权限 group.permission ---->  对比视图函数权限  view(permission=???)   检查成功则说明有这个权限,继续访问这个视图

    RootFacory里就是要查出哪个组有哪个权限的信息,然后告诉pyramid这个框架

    给item视图加权限

    # -*- coding:UTF-8 -*-
    from pyramid.response import Response
    from pyramid.view import view_config, view_defaults
    from myshop.lib import category
    from base import CBase
    
    ctrl = 'item'
    
    # @view_config(route_name='home', renderer='templates/mytemplate.pt')
    @view_defaults(route_name='/')
    class item(CBase):
        def __init__(self, request):
            CBase.__init__(self, request)
            self.request.title = u'商品'
    
        @view_config(match_param=('ctrl=%s' % ctrl, 'action=view'),
                     renderer="item.html", permission='item')
                     # renderer="string")
        def view(self):
            id = self.request.params.get('id')  # 接收id
            item_info = category.get_item_by_id(id)
            return {'item':item_info}

    登录有权限的账号发现

     如图:如果没有添加商品得权限则不让添加商品按钮显示

    model.py中给user添加校验方法

    class User(Base):
        __tablename__ = 'users'
        id = Column(Integer, primary_key=True)  # primary_key 主键
        name = Column(Unicode(255), nullable=False, unique=True)  # nullable=False
                                                                  # 不允许为空 unique 唯一
        password = Column(Unicode(255), nullable=False)
        email = Column(Unicode(255), unique=True)
        group_id = Column(Integer, ForeignKey('groups.id'), nullable=False)  # 关联外键groups的id
        group = relationship('Group', backref='users')
        # 可以使用User的实例对象+".group"查询Group的数据,
        #  backref可以使用Group的实例对象查询User的数据,
        # 以列表形式返回 相当于在Group里写users = relationship('User')
    
        def has_permission(self, permission):
            # import pdb;pdb.set_trace()
            for perm in self.group.permissions:
                if perm.name == permission:
                    return True
            return False

    然后再视图模板中调用该方法

    <ul class="menu_r">
                <li><a href="${request.route_path('index', ctrl='', action='',pa=())}">首页</a></li>
                % if request.title==u'分类':
                    % if request.user.has_permission('item'):
                        <li><a href="#"  onclick="itemAdd()">添加商品</a></li>
                    % endif
                % endif
            </ul>

    单步调试

    在需要调试的地方引用pdb;pdb_set_trace()

    此处__init.py中的groupfinder方法需要调试

    def groupfinder(userid, request):
        """每当用户登录的时候,每当认证机制检查用户是否存在时都会调用下这个函数"""
        import pdb;pdb.set_trace()
        user_info = user.get_user_by_id(userid)  # 查询登录账户信息
        if user_info:
            return [user_info.group.id]
        return None

    登录账号然后查看终端

     此处可以打印下id等查看

    也可以输入n进行下一步,一步一步调试

    调试后发现返回的只是一个用户组的id,且id是整型 与RootFactory中__acl__定义的不一致

    def groupfinder(userid, request):
        """每当用户登录的时候,每当认证机制检查用户是否存在时都会调用下这个函数"""
        print("*" * 100)
        user_info = user.get_user_by_id(userid)  # 查询登录账户信息
        if user_info:
            return [user.group.id]  # 与__acl__定义的标识符不一致
        return None
    
    class RootFactory(object):   # 创建RootFactory类
        def __init__(self, request):
            """读取所有权限"""
            group_list = user.get_group_list()  # 查询所有的组
            self.__acl__ = []  # 准备acl列表
            for group in group_list:
                for permission in group.permissions:
                    # 给acl列表添加元祖,  1.Allow 允许或拒绝 需要导入Allow库 2.为了不和其它id冲突 添加g:的标识符 3.权限名称
                    self.__acl__.append(
                        (Allow, 'g:' + str(group.id), permission.name)  
                    )

    修改为

    def groupfinder(userid, request):
        """每当用户登录的时候,每当认证机制检查用户是否存在时都会调用下这个函数"""
        # import pdb; pdb.set_trace()  # 断点调试
        user_info = user.get_user_by_id(userid)  # 查询登录账户信息
        if user_info:
            return ['g:' + str(user_info.group.id)]  # 与__acl__定义的标识符保持一致
        return None

    可以进入页面了

     登录时把user信息存入request

    __init__.py配置config

    def get_user(request):
        user_id = unauthenticated_userid(request)
        user_info = user.get_user_by_id(user_id)
        return user_info
    
    def main(global_config, **settings):
        """ This function returns a Pyramid WSGI application.
        """
        .
        .
        .
         # 添加授权机制
        config.set_authorization_policy(authz_policy)
        # 添加用户信息到request
        config.set_request_property(get_user,  # 回调函数
                                    'user',   # 此处写user就是request.user 写u就是request.u
                                    reify=True)  # 为True的时候会把登录用户保存下来 不需要每次区查询

     html模板里用request.user渲染数据

    <span  class="fl">
             ${request.user.name},欢迎您的到来
    </span>

     DBSession存入request

    # 添加DBSession到request
        config.set_request_property(lambda request: DBSession,
                                    'db',
                                    reify=True)

    用的时候需要传入request

    def get_category_list(request):
        result = request.db.query(Category).filter_by(parent=None).all()
        return result

     ValueError: renderer was passed non-dictionary as value:渲染器以非字典形式作为值传递

    做添加商品页面时出现错误

    视图函数中item.py

     @view_config(match_param=('ctrl=%s' % ctrl, 'action=item_add'),
                     renderer="itemadd.html")
        def item_add(self):
    
            category_id = self.request.params.get('category_id','')
            print(category_id)
            return category_id

     解决方法-使用字典传值

     @view_config(match_param=('ctrl=%s' % ctrl, 'action=item_add'),
                     renderer="itemadd.html")
        def item_add(self):
            result = {}
            result['category_id'] = self.request.params.get('category_id', '')
            print(result)
            return result
  • 相关阅读:
    段落排版--对齐
    1055. The World's Richest (25)
    1054. The Dominant Color (20)
    (八十一)利用系统自带App来实现导航
    (八十)MapKit放置系统默认大头针和自定义大头针
    (七十九)MapKit的基本使用
    1052. Linked List Sorting (25)
    (七十八)使用第三方框架INTULocationManager实现定位
    (七十七)地理编码与反地理编码
    1051. Pop Sequence (25)
  • 原文地址:https://www.cnblogs.com/yifengs/p/12243553.html
Copyright © 2020-2023  润新知