9_17 django数据库表关联,路由和视图

一。数据库的关系建立。

  在原生的数据库语句中,建立表与表之间的联系,就是添加一个字段,将联系的表的id值添加到该字段中。

  django所作的也就是这些。

  以图书管理系统为例,图书管理系统有四张表:书籍表,作者表,出版社表,书籍细节表。他们之间的对应关系如下:

  书籍对应书籍细节表是一对一。

  书籍对应作者表是多对多。

  出版社对应书籍表是一对多。

  这些在django都有相关的字段或者虚拟字段来建立。

  1。建立一对多的关系。

  对于一对多的字段需要将其添加到多的那张表中,也就是书籍表:

class Book(models.Model):
    title = models.CharField(max_length=64)
    price=models.DecimalField(max_digits=8,decimal_places=2)
    publish = models.ForeignKey(to='Publish')
    authors = models.ManyToManyField(to='Author')

  其中关键字ForeignKey,外键,就默认将PUblish中的数据的id添加到外键中。

  其中外键也可以这样写:

publish = models.ForeignKey(to=Publish)

  直接将对象作为参数传入,但是需要将该参数写在上面,因为解释器是从上往下编译。

  补充:max_digits是指整数位的显示位数,decimal_places是小数位显示的个数。

  2.多对多。

  多对多的情况只需要在两表中的一个建立ManyToManyField即可,建立的不是真正的字段,是一个虚拟的。会重新建一个表表示两者的对于关系。

  3.一对一。

  一对一的关系体现在书籍和书籍详情上,可以使用关键字:OneToOneField字段进行绑定,一般绑定在查询频率较高的表中。

class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDetail',null=True)

  在foreignkey中,字段后面会自动添加_id作为区分。无论你原来有没有。

二。django的生命周期。

  

   一般的,从前端的请求发送都会发送到wsgiref模块进行处理,然后从路由urls中寻找相应视图函数,最后通过模板和数据库的渲染,返回到页面。

三。路由匹配。

  当前端发送数据请求时通常携带请求信息等,其中就包括路,路由决定了后端提交给前端的模板,通过路由可以搜寻到相关的视图函数。

  在django中,路由匹配通常是url开头的以正则表达式为基础的匹配。

  当正则表达式匹配上路由时,就不会往下继续匹配。

  1.加斜杠二次匹配。 

  当在浏览器中没有加斜杠就进行匹配而没能匹配到结果时,会自动加上/再次进行匹配。如果没有比配到就会报错。

  如果需要取消这一机制,需要在settings配置文件:

APPEND_SLASH = False  # 该参数默认是True

  通常,如果需要精准匹配路由,都会在首和尾添加符号^$。

  2,^$应用。

  我们可以通过正则表达式^$来匹配初始页面,用来渲染该项目的初始页面。

  3.''空应用。

  在匹配的最后可以加上空字节匹配所有路由,当第一次匹配,没有任何结果时就返回错误页面渲染,但是这样就不能使用第一条的二次匹配了。

四。有名分组与无名分组。

  在url路由匹配时,由于匹配的性质时正则表达式,所以,可以在正则表达式中书写分组。

  复习:在正则表达式中,出现了分组时()findall就会事先展示分组中的内容。

  无名分组。

  而在这里,分组匹配的内容会作为位置参数传给它的视图函数,所以,视图函数中需要指定一个位置参数来接受该值。

def test(request,xx,year):
    print(year)
    print(xx)
    return HttpResponse('test')

url(r'^test/([0-9]{4})/', views.test)

  有名分组:

  当正则表表达式给分组中匹配的值起了别名后,就会像视图传入一个关键字参数,所以后端需要传入相应的名字的参数作为接受。

url(r'^test/(?P<year>d+)/', views.test)

  注意:无名分组和有名分组不能混合使用,即不能出现以下情况:

url(r'^test/(d+)/(?P<year>d+)/', views.test),

  但是同一种分组下支持多个使用:

    # 无名分组支持多个
    # url(r'^test/(d+)/(d+)/', views.test),
    # 有名分组支持多个
    # url(r'^test/(?P<year>d+)/(?P<xx>d+)/', views.test),

五,反向解析

  对于反向解析,就是返回一个能够访问对应url的地址给后端,所以需要匹配正则。

  反向解析可以让后端和前端动态的拿到url。

  首先需要对对应的url起别名。:

url(r'^index/$',views.index,name='kkk')

  1.前端反向解析:

  通过以下函数可以在前端拿到对应 的url:

{% url 'kkk' %}

  2.后端反向解析:

from django.shortcuts import 
render,HttpResponse,redirect,reverse
reverse('kkk')

  注意,这个函数是根据别名拿对应的url即使不是本函数的url也可以拿到!

  3.无名分组反向解析。

  当url中出现正则表达式有分组的出现,其需要匹配一定的数值才能拿到该url,具体如下。

url(r'^index/(d+)/$',views.index,name='kkk')

  直接通过kkk是不能获取对应的url的,需要添加一定的参数:

后端反向解析
reverse('kkk',args=(1,))  # 后面的数字通常都是数据的id值
前端反向解析
{% url 'kkk' 1%}   # 后面的数字通常都是数据的id值

  一般的,后端反向解析都是用来跳转页面用的,前端都是用来传递关键信息(猜测)。

  4。有名分组反向解析。

  当有名分组时,需要反向解析其实和无名可以公用一个方法:

url(r'^index/(?P<year>d+)/$',views.index,name='kkk')

  后端反向解析:

print(reverse('kkk',args=(1,)))

  可以使用关键字传参:

print(reverse('kkk',kwargs={'year':1}))

  前端反向解析:

<a href="{% url 'kkk' 1 %}">1</a>

  关键字传参:

<a href="{% url 'kkk' year=1 %}">1</a>

  注意:在同一个应用下,别名不能重复。

六。路由分发。

  当你的django项目特别庞大的时候 路由与视图函数对应关系特别特别多,那么你的总路由urls.py代码太过冗长 不易维护。

  每一个应用都可以有自己的urls.py,static文件夹,templates文件夹。所以在app中创建这些文件即可。

  项目下的urls只需要分发路由即可:

from app01 import urls as app01_urls
from app02 import urls as app02_urls
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^app01/',include(app01_urls)),
url(r'^app02/', include(app02_urls))
]

  在项目中再调用相应的函数执行:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
]

  当然,django支持简写,也就是导入模块,直接以字符串代替模块名

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app01/',include('app01.urls')),
    url(r'^app02/',include('app02.urls')),

]

  总路由中 一级路由的后面千万不加$符号

七。名称空间。

  在多个app中,如果多个路由起了相同的别名,在视图进行反向解析是,会是哪个呢。

  答案是只能返回最后一个app中的那个别名对应的路由。

  解决方法1:

  在路由分发时,给其定义一个名称空间,在解析时,加上名称空间即可区分:

总路由
url(r'^app01/',include('app01.urls',namespace='app01'))
url(r'^app02/',include('app02.urls',namespace='app02'))

  后端解析:

reverse('app01:index')
reverse('app02:index')

  前端解析:

{% url 'app01:index' %}
{% url 'app02:index' %}

  解决方法2:

  在其名字时不要冲突即可,在起别名时通常将app名字加在前面:

name = 'app01_index'
name = 'app02_index'

八。伪静态。

  静态网页:数据是写死的 万年不变。

  伪静态网页的设计是为了增加百度等搜索引擎seo查询力度。

  所有的搜索引擎其实都是一个巨大的爬虫程序。

  这是一个网站优化相关的知识,通过伪静态确实可以提高你的网站被查询出来的概率,但是再怎么优化也抵不过RMB玩家。

  该方法就是在网站后面加(.html)

九。虚拟环境。

  一般情况下,每一个项目都会配置本地环境给它,就是装载,相同的模块,就是本地环境。

  而每一个项目需要不同的环境运行时,就需要虚拟环境为每个项目配置不同 的环境。

  在虚拟环境创建时,会重新下载一个全新的解释器。

十。Django1.x和2.x的区别  

  路由区别:

  路由层1.X用的是url,而2.X用的是path。

  2.X中的path第一个参数不再是正则表达式,而是写什么就匹配什么 是精准匹配。

  在2.x中也有re_path与url的使用方式一样。

  2.x中的re_path就是1.X的url

  默认的转换器。

  虽然2.x版中path不支持正则表达式,但是它提供了五种默认的转换器。

  1.0版本的url和2.0版本的re_path分组出来的数据都是字符串类型。

  默认有五个转换器:

  str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式。

  int,匹配正整数,包含0。

  slug,匹配字母、数字以及横杠、下划线组成的字符串。

  uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。

  path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)

  用法:

path('index/<int:id>/',index)  # 会将id匹配到的内容自动转换成整型

  其中id可以传到后端去。

  path还支持自定义的转化器:

class FourDigitYearConverter:  
    regex = '[0-9]{4}'  
    def to_python(self, value):  
        return int(value)  
    def to_url(self, value):  
        return '%04d' % value  占四位,不够用0填满,超了则就按超了的位数来!
register_converter(FourDigitYearConverter, 'yyyy')  
urlpatterns = [  
    path('articles/2003/', views.special_case_2003),  
    path('articles/<yyyy:year>/', views.year_archive),  
    ...  
]

十一。视图层。

  1,视图层三个常用的函数:

1.HttpResponse
2.render
3.redirect

  django视图函数必须要给返回一个HttpResponse对象。因为其他的方法内部都是返回的HttpResponse。

  2.JsonReponse。

  JsonReponse函数主要是返回一个json后的对象给前端,需要使用这个函数。

from django.http import JsonResponse
    def index(request):
        data = {'name':'名字','password':123}
        l = [1,2,3,4,5,6,7,8]
        # res = json.dumps(data,ensure_ascii=False)
        # return HttpResponse(res)
        # return JsonResponse(data,json_dumps_params={'ensure_ascii':False})
        return JsonResponse(l,safe=False)  
# 如果返回的不是字典 只需要修改safe参数为false即可

  这个等同于使用json模块转换后使用httpresponse传输。

  其中,如果需要传输的数据中有中文,在json中需要加关键字参数:ensure_ascii=False。

  在这个模块需要将这个字段作为字典传入,内部会自动解析:

JsonResponse(data,json_dumps_params= {'ensure_ascii':False})

  如果要传字典以外的参数,需要将safe参数设置为false。

  一般的,在开发者模式中,有前后端分离的开发者模式,需要后端将参数作为字典传给前端供其处理。

  补充:

  前端处理json数据:

  JSON.stringify()     json.dumps()
  JSON.parse()      json.loads()

  3.上传文件。

  在上传文件时,有些事项需要注意:

  1.enctype需要由默认的urlencoded变成formdata。

  2.method需要由默认的get变成post。

  在后端获取文件时,并不是在POST中获取文件,而是在request.FILES中获取文件。

def down(request):
    if request.method == 'POST':
        file_obj = request.FILES.get('my_file')
        with open(file_obj.name,'wb') as f:
            for i in file_obj.chunks():
                f.write(i)
        return HttpResponse('收到了')
    return render(request,'down_load.html')

  其中chunks()是将其变成可迭代对象,file也是一个可迭代对象,所以可以不加。

  FILES中取值方式和GET中一样,取出列表中的最后一个。

  其他request中的参数(部分):

request.method  #判断get  post
request.GET    
request.POST
request.FILES
request.path  # 只回去url后缀 不获取?后面的参数
request.get_full_path()  # 后缀和参数全部获取      
request.body #原生的二进制数据

    RBAC(role based access control)

    基于角色的权限管理。

    需要对用户访问的路由进行限制。

原文地址:https://www.cnblogs.com/LZXlzmmddtm/p/11537821.html