【问题标题】:Favorite Django Tips & Features?最喜欢的 Django 提示和功能?
【发布时间】:2010-10-07 17:30:31
【问题描述】:

受问题系列“...的隐藏功能”的启发,我很想知道您最喜欢的 Django 技巧或您知道的鲜为人知但有用的功能。

  • 请在每个答案中只包含一个提示。
  • 如果有,请添加 Django 版本要求。

【问题讨论】:

    标签: python django hidden-features


    【解决方案1】:

    我只是从我自己的提示开始:)

    在 settings.py 中使用 os.path.dirname() 来避免硬编码的目录名。

    如果您想在不同的位置运行项目,请不要在 settings.py 中硬编码路径。如果您的模板和静态文件位于 Django 项目目录中,请在 settings.py 中使用以下代码:

    # settings.py
    import os
    PROJECT_DIR = os.path.dirname(__file__)
    ...
    STATIC_DOC_ROOT = os.path.join(PROJECT_DIR, "static")
    ...
    TEMPLATE_DIRS = (
        os.path.join(PROJECT_DIR, "templates"),
    )
    

    致谢:我从截屏视频“Django From the Ground Up”中得到了这个提示。

    【讨论】:

    • 您不应该对回答自己问题的人投反对票。鼓励这样做,即使它是预先确定的。
    • 这是个好主意,我仍然很难理解为什么它不是默认的。有多少人在同一台机器上测试和部署?
    • 这可以让你从总是输入 os.path.join() 中解脱出来,这会很快让人很烦:j = lambda filename: os.path.join(PROJECT_DIR, filename)。然后你只需要输入j("static")
    • 如果您在 Windows 上,则替换反斜杠:os.path.join(PROJECT_DIR, "templates").replace('\\','/')
    • 如果你真的想在 Django 中解决这个问题,请在code.djangoproject.com/ticket/694 留言,要求核心开发人员重新考虑wontfix 的决定。
    【解决方案2】:

    安装 Django Command Extensionspygraphviz 然后发出以下命令以获得非常漂亮的 Django 模型可视化:

    ./manage.py graph_models -a -g -o my_project.png
    

    【讨论】:

    • 很好,无法让 pygraphviz 在 Windows 中正确安装,但仍然可以使用 graphviz 从点文件中进行转换。
    • 我喜欢用这个+1分享模型图
    • 这个有svg选项吗?
    • 输出图像现在​​似乎被破坏了
    【解决方案3】:

    使用django-annoying's render_to 装饰器代替render_to_response

    @render_to('template.html')
    def foo(request):
        bars = Bar.objects.all()
        if request.user.is_authenticated():
            return HttpResponseRedirect("/some/url/")
        else:
            return {'bars': bars}
    
    # equals to
    def foo(request):
        bars = Bar.objects.all()
        if request.user.is_authenticated():
            return HttpResponseRedirect("/some/url/")
        else:
            return render_to_response('template.html',
                                  {'bars': bars},
                                  context_instance=RequestContext(request))
    

    编辑指出返回 HttpResponse(例如重定向)会使装饰器短路并按预期工作。

    【讨论】:

    • @becomingGuru - 它会自动发生。
    • 这很好,除非您返回一些 HttpResponseRedirect() 和一些 render_to_response()。然后重定向失败。
    • 我不喜欢它。 “显式胜于隐式”。装饰器并没有告诉它何时会渲染到。
    • @Matthew Schinckel 它实际上并没有搞乱重定向——如果你返回一个 HttpResponse 对象,它只是传递它而不修改它
    • 我相信这种方法现在在 Django 1.3 中是多余的,请参阅 django.shortcuts.render() docs.djangoproject.com/en/dev/topics/http/shortcuts/#render
    【解决方案4】:

    我在整个网站的模板中都使用了一组自定义标签。寻找一种自动加载它的方法(DRY,记得吗?),我发现了以下内容:

    from django import template
    template.add_to_builtins('project.app.templatetags.custom_tag_module')
    

    如果您将其放入默认加载的模块中(例如您的主 urlconf),您将在任何模板中使用自定义标签模块中的标签和过滤器,而无需使用 {% load custom_tag_module %}

    传递给template.add_to_builtins()的参数可以是任何模块路径;您的自定义标签模块不必存在于特定的应用程序中。例如,它也可以是项目根目录中的模块(例如'project.custom_tag_module')。

    【讨论】:

    • @Steef,你为我节省了大量时间/心痛/字节,谢谢。
    • 真的很好。谢谢。自定义标签的存储库也可以很好地分享东西,你不觉得吗?
    • 这很好,除非其他人必须维护您的代码。思考:“最小魔法原则”
    【解决方案5】:

    Virtualenv + Python = 如果您正在处理多个 Django 项目,并且它们有可能不依赖于同一版本的 Django/应用程序,则它们是救生员。

    【讨论】:

    • 你能添加一些使用 django 的 virtualenv 教程链接吗?
    • @BozoJoe:在您的终端中执行此操作:virtualenv myNewEnv --no-site-packages; . myNewEnv/bin/activate; pip install django;它只是工作!
    【解决方案6】:

    不要对您的网址进行硬编码!

    改用url names,并使用reverse 函数来获取URL 本身。

    定义 URL 映射时,为 URL 命名。

    urlpatterns += ('project.application.views'
       url( r'^something/$', 'view_function', name="url-name" ),
       ....
    )
    

    确保每个 URL 的名称都是唯一的。

    我通常有一个一致的格式“项目-应用程序-视图”,例如"cbx-forum-thread" 用于线程视图。

    更新(无耻盗用ayaz's addition):

    此名称可用于带有url tag 的模板。

    【讨论】:

    • 我 100% 同意这一点。我开始使用硬编码的 url,当我稍微改变 url 格式以适应一些变化时,它让我在一个项目中陷入困境。我花时间回去挖掘所有内容并替换硬编码的网址。我唯一的大抱怨是 url 标签错误会杀死整个页面,而硬编码只会弄乱单个链接。
    • 这不应该是隐藏功能,这是最佳实践,也是飞行的唯一途径。
    • @skyl 这不是“唯一的飞行方式”。我参加了 Django 开发冲刺,Adrian Holovaty(Django 的创建者之一)说他甚至不使用url 标签……他的立场是,网址无论如何都不应该改变(如果你想对您的用户友好)。
    • 您也可以在模板中使用它,如{% url path.to.view.name arg1 arg2 %} docs.djangoproject.com/en/dev/ref/templates/builtins/…
    • 如果您使用 jinja2,只需像这样添加 reverse environment.filters['url'] = django.core.urlresolvers.reverse 并且您可以像这样在模板中使用它:{{ 'view-name'|url(arg1, arg2)|e }}(需要“e”来转义某些字符以包含在内在 HTML 中)
    【解决方案7】:

    使用django debug toolbar。例如,它允许查看渲染视图时执行的所有 SQL 查询,您还可以查看其中任何一个的堆栈跟踪。

    【讨论】:

      【解决方案8】:

      不要编写自己的登录页面。如果您使用的是 django.contrib.auth。

      真正肮脏的秘密是,如果您还使用 django.contrib.admin,并且 django.template.loaders.app_directories.load_template_source 在您的模板加载器中,您也可以免费获得模板!

      # somewhere in urls.py
      urlpatterns += patterns('django.contrib.auth',
          (r'^accounts/login/$','views.login', {'template_name': 'admin/login.html'}),
          (r'^accounts/logout/$','views.logout'),
      )
      

      【讨论】:

      • 酷!我不知道我们可以重用管理员登录页面。谢谢!
      【解决方案9】:

      上下文处理器很棒。

      假设您有不同的用户模型,并且您想要包含 在每一个回应中。而不是这样做:

      def myview(request, arg, arg2=None, template='my/template.html'):
          ''' My view... '''
          response = dict()
          myuser = MyUser.objects.get(user=request.user)
          response['my_user'] = myuser
          ...
          return render_to_response(template,
                                    response,
                                    context_instance=RequestContext(request))
      

      上下文进程使您能够将任何变量传递给您的 模板。我通常把我的放在'my_project/apps/core/context.py:

      def my_context(request):
          try:
              return dict(my_user=MyUser.objects.get(user=request.user))
          except ObjectNotFound:
              return dict(my_user='')
      

      在您的settings.py 中将以下行添加到您的TEMPLATE_CONTEXT_PROCESSORS

      TEMPLATE_CONTEXT_PROCESSORS = (
          'my_project.apps.core.context.my_context',
          ...
      )
      

      现在每次发出请求时,它都会自动包含my_user 键。

      还有signalswin。

      几个月前我写了一篇关于这个的博客文章,所以我只是剪切和粘贴:

      开箱即用的 Django 为您提供了几个信号 非常有用。你有能力做事前和 发布保存、初始化、删除,甚至在请求正在处理时 处理。所以让我们远离概念和 演示如何使用这些。假设我们有一个博客

      from django.utils.translation import ugettext_lazy as _
      class Post(models.Model):
          title = models.CharField(_('title'), max_length=255)
          body = models.TextField(_('body'))
          created = models.DateTimeField(auto_now_add=True)
      

      因此,您想以某种方式通知众多博客 ping 中的一个 服务我们已经发了一个新帖子,重建最新的 发布缓存,并发布关于它的推文。好吧,你有信号 无需添加任何内容即可完成所有这些操作的能力 Post 类的方法。

      import twitter
      
      from django.core.cache import cache
      from django.db.models.signals import post_save
      from django.conf import settings
      
      def posted_blog(sender, created=None, instance=None, **kwargs):
          ''' Listens for a blog post to save and alerts some services. '''
          if (created and instance is not None):
              tweet = 'New blog post! %s' instance.title
              t = twitter.PostUpdate(settings.TWITTER_USER,
                                     settings.TWITTER_PASSWD,
                                     tweet)
              cache.set(instance.cache_key, instance, 60*5)
             # send pingbacks
             # ...
             # whatever else
          else:
              cache.delete(instance.cache_key)
      post_save.connect(posted_blog, sender=Post)
      

      我们开始了,通过定义该函数并使用 post_init 信号将函数连接到 Post 模型 保存后执行。

      【讨论】:

      • 在比较 web 框架时,Django 的 Signals 是我这些天的必备功能。例如,编写一个松散耦合的论坛,它可以监听来自“签名”模块的更新,但实际上并不需要该模块工作,并且也可以与实现相同功能的兼容模块一起工作,这很棒。我不知道为什么信号没有更广为人知和流行。
      • 如果我们在项目中使用一些可重用的应用程序,信号对于避免紧密耦合和代码混乱非常重要。您为 django 应用程序的松散耦合提供了一个很好的示例,为此 +1。
      • 你知道信号是否异步吗?
      • “假设您有一个不同的用户模型,并且您希望在每个响应中都包含它。” - 将用户放入session。这样可以为您节省每次请求的数据库命中率。
      • 信号的调用是同步的。在我看来,某种异步工作机制更适合,比如说,在 Twitter/Facebook/etc 上发布(即 rabbitmq),所以 out 网站的用户不会在请求时挂起。
      【解决方案10】:

      刚开始的时候,不知道有Paginator,一定要知道它的存在!!

      【讨论】:

      • :D 对我来说也一样!我花了几天的时间来分页!
      【解决方案11】:

      使用IPython 在任何级别跳转到您的代码并使用 IPython 的强大功能进行调试。安装 IPython 后,只需将此代码放在要调试的任何位置:

      from IPython.Shell import IPShellEmbed; IPShellEmbed()()
      

      然后,刷新页面,转到您的 runserver 窗口,您将进入一个交互式 IPython 窗口。

      我在 TextMate 中设置了一个 sn-p,所以我只需键入 ipshell 并点击选项卡。没有它我活不下去。

      【讨论】:

      • 最好安装ipdb,然后输入ipdb.set_trace()
      • 或者使用 Eclipse / PyDev 的调试器。 :-)
      • 导入ipdb; ipdb.set_trace() FTW!
      【解决方案12】:

      运行一个开发 SMTP 服务器,它只会输出发送给它的任何内容(如果您不想在开发服务器上实际安装 SMTP。)

      命令行:

      python -m smtpd -n -c DebuggingServer localhost:1025
      

      【讨论】:

      • 您可以在 django 1.2 中使用控制台和文件电子邮件后端实现相同目的
      • 优秀!非常适合注册! +1
      • Django 1.2 中的替代方案,设置为:EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' ..将电子邮件打印到manage.py 输出。
      【解决方案13】:

      来自django-admin documentation

      如果您使用 Bash shell,请考虑安装 Django bash 完成脚本,该脚本位于 Django 发行版的 extras/django_bash_completion 中。它启用了django-admin.pymanage.py 命令的制表符补全功能,例如,您可以...

      • 输入django-admin.py
      • 按 [TAB] 查看所有可用选项。
      • 输入sql,然后输入[TAB],查看名称以sql开头的所有可用选项。

      【讨论】:

      • 这比我预期的更有用。谢谢!
      • 这是默认情况下至少在较新的 Ubuntu 中。 :-) 当它第一次出现时,我感到很惊讶。
      【解决方案14】:

      django_extensions 附带的./manage.py runserver_plus 设施真的很棒。

      它创建了一个增强的调试页面,除其他外,使用 Werkzeug 调试器为堆栈中的每个点创建交互式调试控制台(见屏幕截图)。它还提供了一种非常有用的便捷调试方法dump(),用于显示有关对象/框架的信息。

      要安装,可以使用pip:

      pip install django_extensions
      pip install Werkzeug
      

      然后将'django_extensions' 添加到settings.py 中的INSTALLED_APPS 元组并使用新扩展启动开发服务器:

      ./manage.py runserver_plus
      

      这将改变您的调试方式。

      【讨论】:

        【解决方案15】:

        我喜欢使用 Python 调试器 pdb 来调试 Django 项目。

        这是学习如何使用它的有用链接:http://www.ferg.org/papers/debugging_in_python.html

        【讨论】:

        • 这是天赐之物。要提供更多信息,只需在代码的任何一行添加:“import pdb; pdb.set_trace()”。刷新您的页面。它会挂起。现在转到运行开发服务器的终端窗口。它现在应该是一个交互式 shell,您可以在其中访问所有变量,就像它们在您粘贴调试代码的代码中一样。
        【解决方案16】:

        当尝试在 Django 和另一个应用程序之间交换数据时,request.raw_post_data 是一个好朋友。使用它来接收和自定义处理,比如 XML 数据。

        文档: http://docs.djangoproject.com/en/dev/ref/request-response/

        【讨论】:

        • 这就是你如何做到的。谢谢,+1
        【解决方案17】:

        在 Django 旁边使用 Jinja2

        如果您发现 Django 模板语言非常受限制(像我一样!),那么您不必拘泥于它。 Django 很灵活,模板语言与系统的其余部分松散耦合,因此只需插入另一种模板语言并使用它来呈现您的 http 响应!

        我使用Jinja2,它几乎就像是 django 模板语言的增强版,它使用相同的语法,并允许您在 if 语句中使用表达式!不再制作自定义 if 标记,例如 if_item_in_list!你可以简单地说%{ if item in list %},或者{% if object.field < 10 %}

        但这还不是全部;它有更多的功能来简化模板的创建,这里我就不一一列举了。

        【讨论】:

        • 我也使用并喜欢 Jinja2,但我发现“contrib”应用程序有一些耦合。特别是,管理工具与 Django 模板紧密相关。此外,我必须在 contrib.auth 中重新创建登录装饰器以使 Jinja2 友好,但不要太难。
        • 不要用jinja2替换模板系统,只是“添加”它,不要删除django模板。自己的视图使用Jinja2,让admin界面继续使用django模板语言。
        • 我非常同意这一点。大多数时候,Django 有限的语法是可以忍受的,但是当你到了制作自定义标签的地步并发现这实际上有多难时,Jinja2 是一股清新的空气
        • 另外,如果你想对模板源进行any元编程,Jinja2 更令人愉快,因为你可以直接访问已解析模板的 AST。遍历 AST 可以轻松完成诸如找出哪些模板扩展了基本模板或在模板源代码块中列出未绑定变量等任务。
        • 谢天谢地,在 Django 1.2 中,IF 标签更加智能
        【解决方案18】:

        在您的视图代码中添加assert False 以转储调试信息。

        【讨论】:

        • 我觉得assert False更直观=D
        • 如果您在 django 开发服务器中运行您的项目,请使用 python 的 pdb 模块。这是一种更强大的调试方式:import pdb; pdb.stack_trace()
        • pdb 非常有用,除非您调试速度很快,否则您的连接可能会超时。
        • 我自己总是使用5 / 0。为什么是五个?不知道。
        • @StephenPaulger 真的吗?我的浏览器(firefox /w firebug)似乎满足于在我调试时等待几分钟以获得响应。
        【解决方案19】:

        这增加了上面关于Django URL names and reverse URL dispatching的回复。

        URL 名称也可以在模板中有效使用。例如,对于给定的 URL 模式:

        url(r'(?P<project_id>\d+)/team/$', 'project_team', name='project_team')
        

        您可以在模板中包含以下内容:

        <a href="{% url project_team project.id %}">Team</a>
        

        【讨论】:

          【解决方案20】:

          由于 Django “视图”只需要是返回 HttpResponse 的可调用对象,因此您可以轻松地创建基于类的视图,例如 Ruby on Rails 和其他框架中的视图。

          有几种方法可以创建基于类的视图,这是我最喜欢的:

          from django import http
          
          class RestView(object):
              methods = ('GET', 'HEAD')
          
              @classmethod
              def dispatch(cls, request, *args, **kwargs):
                  resource = cls()
                  if request.method.lower() not in (method.lower() for method in resource.methods):
                      return http.HttpResponseNotAllowed(resource.methods)
                  try:
                      method = getattr(resource, request.method.lower())
                  except AttributeError:
                      raise Exception("View method `%s` does not exist." % request.method.lower())
                  if not callable(method):
                      raise Exception("View method `%s` is not callable." % request.method.lower())
                  return method(request, *args, **kwargs)
          
              def get(self, request, *args, **kwargs):
                  return http.HttpResponse()
          
              def head(self, request, *args, **kwargs):
                  response = self.get(request, *args, **kwargs)
                  response.content = ''
                  return response
          

          您可以在基本视图中添加各种其他内容,例如条件请求处理和授权。

          设置好视图后,您的 urls.py 将如下所示:

          from django.conf.urls.defaults import *
          from views import MyRestView
          
          urlpatterns = patterns('',
              (r'^restview/', MyRestView.dispatch),
          )
          

          【讨论】:

          • FWIW,django 作者实际上在一些地方使用基于类的视图,例如contrib.formtools:code.djangoproject.com/browser/django/trunk/django/contrib/…
          • 如果你添加一个 call 方法,你可以创建一个名为 RestfulResource 的类,然后让你的 urls.py 指向实例。
          • 新的(Django 1.3?)通用视图是基于类的。
          【解决方案21】:

          不要使用render_to_response 将上下文绑定到模板并呈现它(这是Django 文档通常显示的),而是使用通用视图direct_to_template。它与render_to_response 做的事情相同,但它也会自动将 RequestContext 添加到模板上下文中,隐式允许使用上下文处理器。您可以使用render_to_response 手动执行此操作,但何必呢?这只是要记住的另一个步骤和另一个 LOC。除了使用上下文处理器之外,在模板中包含 RequestContext 还允许您执行以下操作:

          <a href="{{MEDIA_URL}}images/frog.jpg">A frog</a> 
          

          这是非常有用的。事实上,一般情况下,对一般视图 +1。 Django 文档大多将它们显示为快捷方式,甚至没有简单应用程序的 views.py 文件,但您也可以在自己的视图函数中使用它们:

          from django.views.generic import simple
          
          def article_detail(request, slug=None):
              article = get_object_or_404(Article, slug=slug)
              return simple.direct_to_template(request, 
                  template="articles/article_detail.html",
                  extra_context={'article': article}
              )
          

          【讨论】:

          【解决方案22】:

          我没有足够的声誉来回复有问题的评论,但重要的是要注意,如果您要使用 Jinja,它不支持模板块名称中的“-”字符,而姜戈可以。这给我带来了很多问题,并浪费了时间来试图追踪它生成的非常模糊的错误消息。

          【讨论】:

          • 一个注释可能适用于也可能不适用于“来自 jinja 的模糊错误消息”。确保在 settings.py 中设置 TEMPLATE_DEBUG = False。出于某种原因,这会在 Jinja 模板中给您带来有意义的错误。
          【解决方案23】:

          webdesign app 在开始设计您的网站时非常有用。导入后,您可以添加它以生成示例文本:

          {% load webdesign %}
          {% lorem 5 p %}
          

          【讨论】:

          • 仅供参考,对于使用 Jinja2 而不是 Django 模板的任何人,您可以执行以下操作:{{lipsum(5) }}
          【解决方案24】:

          django.db.models.get_model 确实允许您在不导入模型的情况下检索它。

          James 展示了它的便利性:"Django tips: Write better template tags — Iteration 4 "

          【讨论】:

          • 很好:哦!每当我有循环依赖时,我都会在这里进行惰性导入。
          【解决方案25】:

          每个人都知道有一个可以使用“manage.py runserver”运行的开发服务器,但你知道还有一个用于提供静态文件(CSS / JS / IMG)的开发视图吗?

          新手总是感到困惑,因为 Django 没有提供任何方法来提供静态文件。这是因为开发团队认为这是现实生活中的 Web 服务器的工作。

          但是在开发的时候,你可能不想设置Apache + mod_wisgi,它很重。然后你可以在 urls.py 中添加以下内容:

          (r'^site_media/(?P<path>.*)$', 'django.views.static.serve',
                  {'document_root': '/path/to/media'}),
          

          您的 CSS / JS / IMG 将在 www.yoursite.com/site_media/ 上提供。

          当然,不要在生产环境中使用它。

          【讨论】:

          • 我在开发模式下使用它,为了确保我不会忘记在生产中关闭它,我将该 URL 规则包装在 DEBUG 中,仅有条件。
          【解决方案26】:

          我从sorl-thumbnails 应用程序的文档中了解到这一点。您可以在模板标签中使用“as”关键字来在模板的其他地方使用调用的结果。

          例如:

          {% url image-processor uid as img_src %}
          <img src="{% thumbnail img_src 100x100 %}"/>
          

          这在 Django 模板标签文档中被提及,但仅参考循环。他们没有说您也可以在其他地方(任何地方?)使用它。

          【讨论】:

          【解决方案27】:

          django.views.generic.list_detail.object_list -- 它为分页提供了所有的逻辑和模板变量(其中之一是我现在写的一千次苦差事)。 Wrapping it 允许您需要的任何逻辑。这个 gem 为我节省了很多小时在“搜索结果”页面中逐个调试错误的时间,并使过程中的视图代码更清晰。

          【讨论】:

          • 您可以在djangobook.com/en/2.0/chapter11 上找到本书关于通用视图章节的新版本。评论上的那个转到了Django pre-1.0版本的书(Django book 1.0)
          【解决方案28】:

          PyCharm IDE 是一个很好的编码环境,尤其是调试环境,内置对 Django 的支持。

          【讨论】:

            【解决方案29】:

            使用xml_models 创建使用 XML REST API 后端(而不是 SQL 后端)的 Django 模型。这非常有用,尤其是在对第三方 API 建模时 - 您将获得与您习惯的所有相同的 QuerySet 语法。您可以从 PyPI 安装它。

            来自 API 的 XML:

            <profile id=4>
                <email>joe@example.com</email>
                <first_name>Joe</first_name>
                <last_name>Example</last_name>
                <date_of_birth>1975-05-15</date_of_birth>
            </profile>
            

            现在在 python 中:

            class Profile(xml_models.Model):
                user_id = xml_models.IntField(xpath='/profile/@id')
                email = xml_models.CharField(xpath='/profile/email')
                first = xml_models.CharField(xpath='/profile/first_name')
                last = xml_models.CharField(xpath='/profile/last_name')
                birthday = xml_models.DateField(xpath='/profile/date_of_birth')
            
                finders = {
                    (user_id,):  settings.API_URL +'/api/v1/profile/userid/%s',
                    (email,):  settings.API_URL +'/api/v1/profile/email/%s',
                }
            
            profile = Profile.objects.get(user_id=4)
            print profile.email
            # would print 'joe@example.com'
            

            它还可以处理关系和集合。我们每天都在大量使用的生产代码中使用它,所以即使它是测试版,它也非常有用。它还有一组很好的存根,您可以在测试中使用它们。

            (免责声明:虽然我不是这个库的作者,但我现在是一名提交者,做了一些小的提交)

            【讨论】:

            • 有趣的项目,继续加油!
            • 谢谢,很方便 :-)
            【解决方案30】:

            使用数据库迁移。使用South

            【讨论】:

              猜你喜欢
              • 2011-01-24
              • 2011-03-31
              • 2017-12-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-06-26
              • 2021-07-10
              • 1970-01-01
              相关资源
              最近更新 更多