Django学习之五:Django 之 注意事项及汇总
目录
django 之 注意事项及汇总
学习django框架,因为框架都是别人封装好的,所以使用起来确实方便;但是由于是别人咀嚼给我们吃的。。。(sorry for using this words),所以抽象程度很高,造成了易用难理解不好记,很多为什么这样做会感到费解,这是锻炼抽象理解能力的时候到了。要理解django框架最好从mtv(mvc)设计模式切入。但是每个点有很多注意事项,所以在这里按照django框架的主要modules分类注意事项。
本文主要记录自己在学习django过程中遇到的问题的记录,有新增会更新。每个表述上可能不是很清晰,见谅。
全局 settings
- 创建的app要在settings中注册定义,不然django将不会去使用这个app。因为django的哲学就是所有‘app’ 是即插即用。一个app可以在多个项目中,只要在项目的setting中注册该app。
- 引用注册到installed_apps列表有两种方式:使用应用目录下的app_name.apps.*configig类 或者直接将应用路径名填入,就到应用文件夹层面为止;
- 只有注册了的app,后面的model,template等会使用注册了app的一些信息,没注册它们是不会使用的;如,template system查找模版,根据app列表。
- 设置输出到控制台的日志,打印出model api所执行时的sql语句,在项目setting中添加:
logging = { 'version': 1, 'disable_existing_loggers': false, 'handlers': { 'console': { 'level': 'debug', 'class': 'logging.streamhandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': true, 'level': 'debug', }, } }
- 配置登录验证装饰器(@login_required),利用该装饰器用户登录状态验证失败会跳转到配置文件指定的url。即设置该参数settings.login_url='/login/'
- 如果使用继承abstractuser类的一个user 模式类,那么使用auth组件,就要在settings中设置一个配置auth_user_model = 'app.usermedole' 点前面是应用名,点后面是自定义的user认证model。这里的orm继承方式是table_per_class继承方式。
- media_root 默认是'', 这个路径是一个文件系统的绝对路径,利用配置文件中的base_dir,通过os.path.join() 来获取到绝对路径。
model模块-模型模块
- 配置数据库时,如果使用的是mysql数据库;由于django2.0默认需要mysqlclient模块;需要安装该模块,不然会启动报异常;替代方案是使用pymysql模块,做法是:安装pymysql,再在settings同目录的__init__.py文件中添加以下代码:
import pymysql pymysql.install_as_mysqldb()
三个manage.py 命令参数makemigrations和migrate:
2.1. makemigrations 将setting.py文件中的apps中的model.py中的‘表对象’ 生成对应的迁移映射文件(存放在app目录下的migrations目录中);生成的映射文件用于对数据库进行建表操作。即用于下一步migrate操作
2.2. migrate 应用映射文件到数据库中,即创建检查数据库中没有映射文件中涉及到的表。
2.3. sqlmigrate 命里可以打印出要执行的sql语句,不会实质的执行。提供执行sql可以帮助我了解做了什么数据库操作,并且可以提供给dba即你心里修改。用法:python manage.py sqlmigrate app_name migrations_file_name对于新建的django项目,第一次执行migration操作会将django自带的auth,session组件需要的表一并创建。因为manage.py会遍历所有注册了的apps,然后构建他们需要但是数据库中没有的表。这其实是一个完整的migration过程。也充分证明了,django项目新建表需要先makemigration操作,才会migrate操作创建成功新建表到数据库。如果不想使用django提供的组件或者说modules,可以在migrate前,将相应的组件app从setting.py文件的installed_app中注释或移除。
orm 让代码与数据库解耦。
对于django.db提供的backends封装的mysqlclient会去检测安装的mysqlclient的版本,如果小于1.3.3会抛出异常,所以要么更新要么到代码中去注释掉检测代码。
我的文件路径:d:\python\python36\lib\site-packages\django\db\backends\mysql\base.py 注释掉下面检测代码: # if version < (1, 3, 3): # raise improperlyconfigured("mysqlclient 1.3.3 or newer is required; you have %s" % database.__version__)
models.py中的model class 之间可以表示通常数据库中存在的关系:many-to-one,one-to-one,many-to-many。虽然创建model class 只写了很少的代码,但是给与了django很多的信息。有了model,django可以:创建数据库模型(创建表)为所在的app应用;创建一个python database-access api for accessing 相应的model class 对象
model 和 表 一定要映射好,如果数据库表改变了,对应的model要改变,并进行makemigrations操作,最好在sqlmigrate看下会不会有sql操作,没有就不必再执行migrate操作。如果是改变了model,需要执行一次全流程:makemigrations-sqlmigrate-migrate 这样可以是映射双方一致,不然可能会出问题。
通过orm创建的表名会默认变为应用app名称下划线加上model名。这些都是可以自定义覆盖的。
- 为什么要分步走操作model到库:
the reason that there are separate commands to make and apply migrations is because you’ll commit migrations to your version control system and ship them with your app; they not only make your development easier, they’re also useable by other developers and in production. read the django-admin documentation for full information on what the manage.py utility can do.
关于model relations 参考官档: for more information on model relations, see accessing related objects. for more on how to use double underscores to perform field lookups via the api, see field lookups. for full details on the database api, see our database api reference.
创建model class 最后好重写_str _()方法,这样数据对象在admin显示的时候会友好点。
通过model class 创建记录,最好通过显示的save()操作。这是开发者尽可能的让sql运行的时间短,sql优化;所以才有了queryset对象select_related()方法的存在。而且底层执行join操作是自动的。而且设置者将每个对象都能访问和它相关的对象。也可以写原生sql语句。
model class 在定义字段时,field的null和blank选项的区别,null是关于数据库中字段是否将空值设成数据库中的null,默认是flase,也就是空值就是空值(特别是字符串类型的字段,因为空字符串值对这些字段是有意义的)。而blank是在允许model object 的字段是否能为空值。默认flase,不能为空。
从查询中得到queryset,特别是在认证的时候,经常会通过queryset[0]想获取model object对象,这是错误的做法,如果认证失败,queryset没有,那么该方法取值就会报错,应该使用queryset.first()取出来。
对于model的datetime字段,会使用use_tz和time_zone确定下的时区来存储时间,和mysql全局时区设置可能无关,因为这个是优先级更高的字段时区设置。
urls模块
- 技术上讲,如:foo.com/bar 和 foo.com/bar/ 是两个不同的urls, and 搜索引擎robots会将他们作为单独的页面。django 应该 make effort to 'normalize' urls so that search-engine robots don't get confused. django 提供append_slash来规范这样的困扰。
append_slash default:true when set to true, if the request url not match any of the patterns in 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 reuqest to be lost. 这是用到一个重定向,这可能造成困扰。 下面是抓包证明django确实做了重定向(win下用使用rawcap抓包)。 所以这里有两个方面的考虑: 1. 如果符合上面的情况,造成了重定向,要考虑重定向带来的后果。同时任何没匹配且没有已/结尾的请求都会有一次重定向给浏览器。 2. 如果将urlsconf中去掉了slash,那么如果要区分就两种情况都要添加到urlsconf中,就不产生重定向了。或者append_slash:false.但是这样就只有严格匹配。无论是用户添加斜杠和urlsconf的route字符串中添加斜杠,将会带来更多的困扰。所以django默认是append_slash:true. 但是django对于没有已斜线结尾且没有url匹配的上会发送一个添加斜杠的url,这是针对没有post数据的情况。如果有post数据时且这个数据还在处理,django将不会自动添加。所以用ajax上传文件,不要依赖django提供的这个功能,要加上应该有的斜杠。
django少斜杠 产生301重定向造成登陆态变化的困扰https://blog.csdn.net/handsomekang/article/details/78650513
django 2.0 使用include中定义命名空间的话,要在被include的url模块中加入app_name属性且属性值为实际app明;或者改变include为include(('app01.urls', 'app01'),namespace = 'app01').不然会报异常。这是怎么回事呢?有空再去看文档。
django2.0 提供path对象来匹配uri。新的path要求将正则表达式封装在一个converter转换器中,converter除了封装有regex还可以做很多类型转换,converter还必须实现两个函数:to_python
和to_url两个方法。之所以提供path,是可以提供一些默认的转换器来替代正则表达式,为一些不会正则表达式的同学使用;同时解决复用问题,通过将正则表达式封装成转换器,转换器就可以多出被引用了,非常科学的path设计。urlpatterns列表中都是path对象或者re_path对象,他们都like-a关系都是有同样的接口,给django来调用是否匹配他们包含的正则表达式。
- include()可以是其它包含urlpattern列表的模块,也可以使一个包含path或re_path对象的列表变量。
from django.urls import include, path from apps.main import views as main_views from credit import views as credit_views extra_patterns = [ path('reports/', credit_views.report), path('reports/<int:id>/', credit_views.report), path('charge/', credit_views.charge), ] urlpatterns = [ path('', main_views.homepage), path('help/', include('apps.help.urls')), # 这是include模块 path('credit/', include(extra_patterns)), # 这是include一个path列表 ]
这里不针对django的url,所有html a标签,表单,包括jquery实现的ajax的url参数中,空的话默认是当前页面的完整url,如果填写了一个uri,这个uri最前面有没有'/'对浏览器来说是很重要的,如果没有,则是当前url+uri;如果有,那么就是当前的ip:port+uri,也就是说,有‘/’会从url的绝对路径访问,没有则是相对当前路径访问。关于django的中提供的反向解析大多都是产生一个绝对路径。
这里区分下django web 框架编程代码中,几个相对路径的意义,在web开发中比较容易混淆:a) 非djangoapi中的相对路径,如open中的相对路径是相对与当前入口代码文件的路径,即main程序,即使在路径前使用一个像linux的/根目录的表示,如/aaa/bbb/,其实也是一个相对路径,也是相对当前文件目录的路径,要是用绝对路径必须从文件驱动器开始。b)
在django的 render函数中,指定模版时,使用的路径是相对于每个注册app下的templates目录作为相对目录,包括settings中指定的额外templates存放目录作为相对参照目录。c) 另一个就是静态文件目录,我么经常在模版中使用static标签指定静态文件,这是指定的静态文件路径,就是相对于每个注册app的static目录,作为相对路径参考路径,包括settings配置staticfiles_dirs中额外指定的目录,类似templates的参照相对路径。以上三种路径在web编程时很容易混淆,所以要在使用路径时特别注意,使用在那种情况下,不同情况的参照路径是不同的。d) 还有就是settings配置文件中的staticfiles_dirs中使用的文件系统绝对路径,media_root也是文件系统的绝对路径。对于settings中 url的路由,有匹配的path或re_path放到urlpatterns列表的后面,把固定匹配的放在列表前面,避免被匹配截胡了。
templates system 模版模块
对于模版系统明确:模版语法;含有模版语法的文本是模版文件;模版文件渲染通过模版系统的render()函数,函数渲染后应该返回一个httpresponse对象;过滤器filter; 两种模版语法的语法形式;两种简单逻辑branch分支和looping循环。django模版系统是后端模版选型,大多页面渲染都是后端做,没有太多前端控制内容,所以很多时候要整个页面刷新或者重定向。模版设计创建可以是python麻瓜。。。就是纯页面设计人员不一定懂python,但是需要的数据信息要在该网站的范畴内,不然django开发人员使用模版时无法提供这些超出范畴的数据。
django框架有自己的模版语法,但也支持来自第三方的模版语言,需要第三发组件。但是使用不信任的开发者的第三发组件用于templates是不安全的,因为可能没有防止恶意注入的功能,导致不安全。
django定义了一个标准的api 用于 loading 和 rendering, templates.loading 就是查找templates 和预处理 templates,通常编译templates到内存中;rendering 向 内存中的templates 插入 环境数据 并放回一个结果字符串。
templates system 查找模板的目录在settting配置文件中。两个参数都可以dirs和app_dirs
get_template() 和 select_template() 后者更加灵活,可用于fallback
注意每个注册了的app, 自己的模版一定要放在一个应用目录下的templates子目录中。
在模版语言中,我们可能常会用到一个{% static %}的标签,这个标签不是一个内置标签,这个是django.contrib.statifiles 应用的自有定义标签,所以要使用该标签必须使用{% load static %} 该应用中的自定义文件,这样后面就可以使用这个标签了。就是这个还要注意在template inheritance继承中,导入的自定义只会在出现导入语句的模版中有效,其子模版中是没有效果的,所以要在子模版也加上这个{% load static %}才能在子模版中使用{% static '' %}引入静态文件路径。经过实验,只有第三方自定义的才需要在父子模块中都要导入才行。dtl中的变量都从子模块能够得到解析。
{% extends 'base.html' %} 继承一个模版这句话必须在模版文件最前面
{% block.super %} 会使用父级模版的内容
view
- 在request中获取post数据时,如果通过request.post获取有checkbox和select表单提交的多个数据时,不能使用get方法而是使用getlist才能取出,不然会取出多个数据的最后一个值,而通过getlist可以得到列表。
cookie and session
- 浏览器的刷新,记住不是简单的访问浏览器地址栏看到的地址,而是重新访问的是,得到当前页面的请求,也就是刷新发送的请求报文和上一个请求报文的method,提交的参数都是一样的。不要被地址栏的蒙骗了,以为就是再次访问地址栏的地址。
这个问题是在登录成功过后,返回一个成功页面,刷新页面发现提交的内容还是登录请求一样,不是访问地址栏的一个get请求。 - django session,如果数据库总的sessionid存在且有效(是否过期?或者其它限定),会沿用sessionid,不会覆盖。这种不会新建sessionid只更新session字典内容,就会存在一个问题:如果换另一用户登录,那么如果只更新用户名,没有更新了上一个用户访问其它页面存放的权限信息,那么这个用户就会得到上一个用户的session中保存的权限内容,这就造成了数据混淆。解决这种情况,只有增加判断,删除sessionid,新加sessionid。django提供了一个比较方便的组件,叫做auth,使用auth进行认证,就会有上面提到的删除老的sessionid记录,创建新的sessionid记录。
file storage api
django 提供了两种方便的方式访问当前的storage class:
- class defualtstorage
- get_storage_class
django中时区
- 配置文件中有 time_zone和 use_tz 这两个,结合在一起将django使用的时间时区有关都表述清楚了。具体就是前者time_zone规定了django中所有api在输出时间是使用显示的时区。而后者则是限制通过model进行数据存储时使用的时区是是否使用time_zone配置的时区。如果use_tz是true则使用utc通过model存储,也就是所通过model获取的datetime时间是utc时间。如果设置为false这model存储获取的datetime时间是time_zone指定的时区时间。所以从model读取的datetime时间是受两个设置所决定的,就是use_tz和time_zone了。
- django的utils提供了一个工具django.utils.timezone.now() 来获取和model相同的时区设置。如果使用datetime.datetime.now() 可能就不能和model中的时间进行时间间隔运算。
上一篇: 1的个数 南阳acm514