Django 进阶篇二
规范
确立规范的好处:
- 代码可读性高
- 方便代码的定位极其查找
- 为以后代码扩容带来便利
场景:
在多个APP的场景下,单个app的URL函数功能较多的时候,我们可以通过以下方法来解决。
把Views写成模块的方式并且为不同的功能进行不同的划分,并且在Templates中使用同样规则,如下图:
我根据不同的html然后创建不同的函数,命名和templates模板目录一样这样非常方便找到,这个页面中的函数在哪里。
设置路由的时候就得导入相应的函数(下面的函数是在app01中的url,通过object的url转发过来):
from cmdb.views import account from cmdb.views import home from cmdb.views import asset urlpatterns = [ #账户操作登录登出 url(r'^login/$', account.login), url(r'^logout/$', account.logout), #home操作 url(r'^index/$', home.index), #资产信息操作 url(r'^lists/$', asset.lists), url(r'^save_hostinfo/$', asset.save_hostinfo), url(r'^del_hostinfo/$', asset.del_hostinfo), url(r'^add/$', asset.add), #default url url(r'', account.login), ]
还有就是Form也是有很多文件的所以我们也要单独创建下!
2、静态文件导入优化
看下面的一种情况首先咱们在导入静态的css和js的时候是怎么导入的?
{#CSS#} <head lang="en"> <meta charset="UTF-8"> <title></title> <link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.min.css"/> <link rel="stylesheet" href="/static/plugins/font-awesome/css/font-awesome.min.css"/> <link rel="stylesheet" href="/static/css/commons.css"/> <link rel="stylesheet" href="/static/css/account.css"/> </head> {#JS#} <script src='/static/js/jquery-1.8.2.min.js'></script> <script src='/static/js/valid.js'></script>
这个static在哪里设置的呢?在object的settings里设置的:
########### 静态文件路径 ########## STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static'), ) ########### 增加'django.core.context_processors.static', 在OPTIONS里 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', 'django.core.context_processors.static', ], }, }, ]
""" ## 查看原理 ## 'django.core.context_processors.static', def static(request): """ Adds static-related context variables to the context. """ return {'STATIC_URL': settings.STATIC_URL} # 这里和模板语言一样返回一个字典 """
那如果以后想把static目录改成content怎么改呢?难道要在每个html中去修改路径吗?这样如果html文件多的话会疯的,所以我们可以这么修改:
如果想修改路径的话直接修改:settings里的 STATIC_URL = '/static/' 指定新的目录即可
{#CSS#} <head lang="en"> <meta charset="UTF-8"> <title></title> <link rel="stylesheet" href="{{ STATIC_URL }}plugins/bootstrap/css/bootstrap.min.css"/> <link rel="stylesheet" href="{{ STATIC_URL }}plugins/font-awesome/css/font-awesome.min.css"/> <link rel="stylesheet" href="{{ STATIC_URL }}css/commons.css"/> <link rel="stylesheet" href="{{ STATIC_URL }}css/account.css"/> <link rel="stylesheet" href="{{ STATIC_URL }}css/login.css"/> </head> {#JS#} <script src='{{ STATIC_URL }}js/jquery-2.1.4.min.js'></script> <script src='{{ STATIC_URL }}js/valid.js'></script>
上面的原理就类似模板语言的渲染一样!
效果如下:
2、还有一种方式
通过simple_tag 的方式进行导入,他不需要注册option即可。注意里面的路径
{#在顶部load 一个staticfiles#} {% load staticfiles %} {#在底部使用#} <script src='{% static "js/jquery-2.1.4.min.js" %}'></script>
Form
首先看下,在Form里和Views里都使用了account很容易造成混乱所以我们可以使用别名来区分:
from cmdb.forms import account as AccountForm
1、Form保存用户输入内容
场景:比如有一个html页面有很多的input标签需要你输入,但是如果你输入错误了,数据已经提交过去了,所有数据需要你在写一遍是不是非常不人性化,看下面的例子:(我故意输入错误了密码之后信息还需要我在重新写一遍)
代码如下:
def login(request): obj = AccountForm.LoginForm() if request.method == 'POST': # 获取用户输入 login_form = AccountForm.LoginForm(request.POST) # 判断用户输入是否合法 if login_form.is_valid(): # 如果用户输入是合法的 username = request.POST.get('username') password = request.POST.get('password') user_info_list = models.UserInfo.objects.all() for i in user_info_list: if username == i.email and password == i.passwords: request.session['auth_user'] = username return redirect('/index/') else: return render(request,'account/login.html',{'model': obj,'backend_autherror':'用户名或密码错误'}) else: error_msg = login_form.errors.as_data() return render(request,'account/login.html',{'model': obj,'errors':error_msg}) # 如果登录成功,写入session,跳转index return render(request, 'account/login.html', {'model': obj})
优化的方法就是:
def login(request): # 获取用户输入 login_form = AccountForm.LoginForm(request.POST) if request.method == 'POST': # 判断用户输入是否合法 if login_form.is_valid(): # 如果用户输入是合法的 username = request.POST.get('username') password = request.POST.get('password') user_info_list = models.UserInfo.objects.all() for i in user_info_list: if username == i.email and password == i.passwords: request.session['auth_user'] = username return redirect('/index/') else: return render(request,'account/login.html',{'model': login_form,'backend_autherror':'用户名或密码错误'}) else: error_msg = login_form.errors.as_data() return render(request,'account/login.html',{'model': login_form,'errors':error_msg}) # 如果登录成功,写入session,跳转index return render(request, 'account/login.html', {'model': login_form})
这里,我们把用户输入的请求封装到Form表单给用户返回回去了!这样用户提交的数据就可以在同样返回给用户!
那有人会问如果不是POST的时候,你也这样返回没有问题吗?没有问题的,没有POST请求的话request.POST是空所以不影响。
效果如下:
2、Form错误信息
Form的错误信息有3种,咱们常用的就是:
error_msg = errors
error_msg = errors.as_json() # 返回字符串形式
error_msg.as_ul很少用
在返回Ajax的请求的时候能返回对象吗?是不可以的!(咱们一般返回字符串,或字符串类型的字典),但是咱们可以通过error_msg = errors.as_json()来返回!
print type(login_form.errors) print type(login_form.errors.as_json())
结果:
<class 'django.forms.utils.ErrorDict'> <type 'str'>
第一个是ErrorDict是Django定义的一个类,第二个是字符串。
看下第一个错误信息,他是一个字典我们可以通过字典来取他的值看下(Views):
error_msg = login_form.errors.as_data() print (error_msg['username'][0]) print (error_msg['password'][0])
然后当我们发送数据的时候通下面的方式发送在模板中我们不能通过字典的方式取值只能通过“.”的方式取字典的值怎么办?所以就需要simple_tag来做了:
return render(request,'account/login.html',{'model': login_form,'errors':error_msg})
#!/usr/bin/env python # -*- coding:utf-8 -*- from django import template from django.utils.safestring import mark_safe from django.template.base import resolve_variable, Node, TemplateSyntaxError register = template.Library() @register.simple_tag def error_message(arg): if arg: return arg[0][0] else: return ''
2、对于Ajax来说
error_msg = login_form.errors.as_json() print error_msg,type(error_msg)
{"username": [{"message": "\u90ae\u7bb1\u8d26\u6237\u4e0d\u80fd\u4e3a\u7a7a", "code": "required"}], "password": [{"message": "\u5bc6\u7801\u4e0d\u80fd\u4e3a\u7a7a", "code": "required"}]} <type 'str'>
之前说过的在error的时候有多个错误,我们默认取第0个就可以了,我们在通过Ajax发送登录认证的时候可以通过返回来的错误信息通过js来进行显示即可!
3、FormSelect更新
看下面的这种情况基本的通过form生成select标签
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>可更新下拉菜单</h1> <p>{{ obj.host }}</p> </body> </html>
form
class ImportForm(forms.Form): HOST_TYPE_LIST = ( (1,'物理机'), (2,'虚拟机'), ) host = forms.IntegerField( widget=forms.Select(choices=HOST_TYPE_LIST) )
views
def index(request): obj = HomeForm.ImportForm(request.POST) return render(request,'home/index.html',{'obj':obj})
这个select里面的选项是写死的就两个选项他是静态的,如果这个是业务线的话,是动态的话,我有新增了一个业务线怎么办?先拿文件举例,后面会跟上从数据库取数据的例子!
有一个文件db_admin通过json.dumps生成的文本
[[1, "物理机"], [2, "虚拟机"]]
然后在form里调用,html&views不变,修改下form
class ImportForm(forms.Form): f = open('db_admin') data = f.read() data_tuple = json.loads(data) f.close() host = forms.IntegerField( widget=forms.Select(choices=data_tuple) )
现在是动态生成了,我现在在db_admin里在增加一个字段
[[1, "物理机"], [2, "虚拟机"],[3, "云主机"]]
当我在刷新页面的时候发现:为什么我添加的‘[3,"云主机"]’,为什么没有出现呢?我已经添加了!
当类在被调用的时候首先执行的是类的构造方法:__init__方法,每当调用类的实例化的时候他都执行!所以我们可以给form定义一个form方法。
class ImportForm(forms.Form): f = open('db_admin') data = f.read() data_tuple = json.loads(data) f.close() host = forms.IntegerField( widget=forms.Select(choices=data_tuple) ) def __init__(self, *args, **kwargs): super(ImportForm, self).__init__(*args, **kwargs) f = open('db_admin') data = f.read() data_tuple = json.loads(data) f.close() # 通过self.fields找到host这个静态字段的.wideget.choices属性并重新给他赋值! self.fields['host'].widget.choices = data_tuple
优化:
class ImportForm(forms.Form): host = forms.IntegerField( widget=forms.Select() # 这里默认是空值,每一次我们使用的时候通过__init__进行赋值 ) def __init__(self, *args, **kwargs): super(ImportForm, self).__init__(*args, **kwargs) #执行当前类的父类的构造方法(__init__) f = open('db_admin') data = f.read() data_tuple = json.loads(data) f.close() # 通过self.fields找到host这个静态字段的.wideget.choices属性并重新给他赋值! self.fields['host'].widget.choices = data_tuple