ORM表关系建立

ORM表关系建立

如何判断表关系:换位思考,先站在一张表上面分析, 然后再站在另一张表上分析,最后再结合

一对一

外键字段创建在任意一张表都可以,建议外键添加在查询频率较高的一方

# 关键字OneToOneField
author_detail = models.OneToOneField(to='Author_detail')  # 外键本质fk + unique

一对多

外键字段创建在多的那一方

# 关键字ForeignKey
publish = models.ForeignKey(to='Publish')  # to用来指代跟哪张表有关系 默认关联的就是表的主键字段
# 外键字段名在创建时会自动加上_id后缀

多对多

外键关系需要创建第三张表来处理。

# 关键字ManyToManyField
author = models.ManyToManyField(to='Author')  
# django orm会自动帮你创建第三张关系表,表名为两个关联的表名用_连接

Django 请求生命周期

img

url 路由层

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

url的第一个参数是一个正则表达式,只要该正则表达式能够匹配到内容,就会立刻执行后面的视图函数,不再往下继续匹配。

路由匹配

Django settings.py配置文件中默认 APPEND_SLASH = True。 其作用就是自动在网址结尾加'/'。

如果要取消django自动让浏览器加斜杠的功能,需要手动写上APPEND_SLASH = False

Django 路由匹配的规律:

  1. 先是不加斜杠,先匹配一次试试
  2. 如果匹配不上,会让浏览器重定向,url最后加上斜杠再次匹配一次
  3. 如果还是匹配不上就报错了
  4. 并且不会匹配GET请求?后面携带的参数

无名分组

将分组内正则表达式匹配到的内容当做位置参数传递给视图函数

url(r'^test/([0-9]{4})/', views.test)
       
# 当你的路由中有分组的正则表达式  那么在匹配到内容
# 执行视图函数的时候 会将分组内正则表达式匹配到的内容当做位置参数传递给视图函数  
def test(request,xxx):
    '''
    此时url有第二个参数,会传递给视图函数位置参数传进来。
    如果不写接收的位置参数则会报错
    test() takes 1 positional argument but 2 were given
    '''
    print(xxx)  # 会接收到url中的数字
    pass

有名分组

将分组内正则表达式匹配到的内容当做关键字参数传递给视图函数

url(r'^test/(?P<year>[0-9]{4})/', views.test)
        
# 当你的路由中有分组的正则表达式  那么在匹配到内容
# 执行视图函数的时候 会将分组内正则表达式匹配到的内容当做关键字参数传递给视图函数  
def test(request,year):
    '''
    此时url有第二个参数,会传递给视图函数关键字参数传进来。
    如果不写接收的关键字参数则会报错
    testadd() got an unexpected keyword argument 'year'
    '''
    print(year) # 会接收到url中的数字,以关键字参数形式显示
    pass

利用有名个无名分组 我们就可以在调用视图函数之前给函数传递额外的参数

注意:无名分组和有名分组不能混合使用

但是同一种分组的情况下 可以使用多次,即无名可以有多个、有名可以有多个

反向解析

给路由匹配设置一个别名,根据这个别名,动态解析出一个结果,该结果可以直接访问对应的url。

路由匹配条件无分组的情况的反向解析

# urls.py:
url(r'^home/',views.home,name='hm'),
url(r'^index/$',views.index),

# views.py:
def home(request):
    return HttpResponse(reverse('hm'))

def index(request):
    print(reverse('hm'))    # /home/
    return render(request,'test.html')

# test.html:
<a href="{% url 'hm' %}">点我</a>


# 当我们在浏览器URL输入http://127.0.0.1:8000/index/的时候,
# python后端进行reverse反向解析成 别名为'hm'的路径前缀,也就是home路径,
# 用test.html 页面渲染,html也可以使用反向解析

# python后端使用反向解析:
    reverse('hm')
# 前端html使用反向解析:
    {% url 'hm' %}
# 将路径解析为/home/。

无名分组情况的反向解析

# urls.py:
url(r'^home/(\d+)/',views.home,name='hm'),
url(r'^index/',views.index),

# views.py:
def home(request,xxx):
    # 使用无名分组,视图函数必须写位置参数
    print(xxx)
    return HttpResponse('ok')

def index(request):
    print(reverse('hm',args=(1,))) 
    # 无名分组需要手动给别名传递一个参数才能匹配上,这个参数会传递到视图函数当做位置参数。
    return render(request,'test.html')

# test.html:
<a href="{% url 'hm' 1 %}">点我点我</a>


'''当一个无名分组使用别名的时候,在浏览器URL输入http://127.0.0.1:8000/index/,python后端进行reverse反向解析成 别名为'hm'的路径前缀,也就是home路径,需要手动给解析出来的路径加参数,不然匹配不成功
用test.html 页面渲染,并且html使用反向解析,也需要手动传入参数'''
        
# python后端使用反向解析:
    reverse('hm',args=(1,))
# 前端html使用反向解析:
    {% url 'hm' 1 %}
# 将路径解析为/home/1。

有名分组的反向解析

# urls.py:
url(r'^home/(?P<year>\d+)/',views.home,name='hm'),
url(r'^index/',views.index),

# views.py:
def home(request,year): 
    # 使用有名分组,视图函数必须写关键字参数
    print(year)
    return HttpResponse('ok')

def index(request):
    print(reverse('hm',args=(1,)))          # 这样也可以,但不正规
    print(reverse('hm',kwargs={"year":1}))  
    # 最正规的写法,因为有名分组传入的是关键字参数,用kwargs参数来表示
    return render(request,'test.html')

# test.html:
<p><a href="{% url 'hm' 1 %}">点我点我</a></p>      # 这样也可以,但不正规
<p><a href="{% url 'hm' year=1 %}">点我点我</a></p> 
# 最正规的写法,因为有名分组传入的是关键字参数


'''当一个无名分组使用别名的时候,在浏览器URL输入http://127.0.0.1:8000/index/,python后端进行reverse反向解析成 别名为'hm'的路径前缀,也就是home路径,需要手动给解析出来的路径加参数,不然匹配不成功。
用test.html 页面渲染,并且html使用反向解析,也需要手动传入参数'''
        
# python后端使用反向解析:
    reverse('hm',kwargs={"year":1})
# 前端html使用反向解析:
    {% url 'hm' year=1 %}
# 将路径解析为/home/1。

例子

伪代码以编辑用户信息为例,演示具体用法:

# urls.py:
url(r'^edit_user/(\d+)',views.edit_user,name='edit'),

# views.py:
def edit_user(request,edit_id):
    ...
    # edit_id 就是用户想要编辑数据的主键值
    return render(request,'edit_user.html',{"user_list":user_list})

# edit_user.hmtl:
{% for user_obj in user_list %}
    <a href="/edit_user/{{ user_obj.id }}">编辑</a>
    <a href="{% url 'edit' user_obj.id %}">编辑</a>
{% endfor %}

路由分发

在django中所有的app都可以有自己的独立的urls.py、templates、static文件夹,那么正是由于这个特点,你的django项目可以由多个人一起开发。小组长最后只需要把所有的开发的app整合到一个空的django项目里面,在settings配置文件里注册就可以了。

路由分发解决的就是项目的总路由匹配关系过多的情况,使用路由分发,会将总路由不再做匹配的活,而仅仅是做任务分发

# 第一种方法
from app01 import urls as app01_urls  # 起别名,避免重复
from app02 import urls as app02_urls  # 或者以app名为前缀,避免重复
urlpatterns = [
    url(r'^app01/',include(app01_urls)),
    url(r'^app02/',include(app02_urls)),
]

# 第二种方法
urlpatterns = [
    url(r'^app01/',include('app01.urls')),
    url(r'^app02/',include('app02.urls')),
]

名称空间

当多个app中出现了起别名冲突的情况,你在做路由分发的时候 可以给每一个app创建一个名称空间,然后在反向解析的时候,可以选择到底去哪个名称空间中查找别名

url(r'^app01/',include('app01.urls',namespace='app01')),
url(r'^app02/',include('app02.urls',namespace='app02'))
        
# 后端
print(reverse('app01:reg'))
print(reverse('app02:reg'))

# 前端
<a href="{% url 'app01:reg' %}"></a>
<a href="{% url 'app02:reg' %}"></a>


# 参考建议
# 起别名的时候统一加上应用名前缀,这样你的别名就不会重复了。
urlpatterns = [url(r'^reg/',views.reg,name='app01_reg')]
urlpatterns = [url(r'^reg/',views.reg,name='app02_reg')]

伪静态

将一个动态网页伪装成一个静态的网页,来提高搜索引擎SEO的查询频率和收藏力度,提高网站的曝光度

# 直接在urls后面手动加上.html
url(r'^article/(\d+).html',views.article)

虚拟环境

虚拟环境就相当于重新下载了一个纯净的python解释器,之后项目用这个虚拟环境,你需要什么就安装什么,与系统环境上存在的软件不冲突。

Django版本区别

urls.py路由匹配的方法有区别。

django 1.X

urlpatterns 中 url 对应的是正则表达式,

from django.conf.urls import url
urlpatterns = [
    url = ('test',view.test)
]

django 2.X

path中第一个参数不支持正则表达式,写了什么就只能匹配什么,匹配不到就报错。

django 2.x中有一个re_path方法,这个方法就是1.X版本中的url()

from django.urls import path,re_path
urlpatterns = [
    path = ('test',view.test),
    re_path = (r'^test/(\d+)',view.test)
]

django 2.X的版本中还提供了五种转换器,

  • str:匹配除路径分隔符/外的字符串
  • int:匹配自然数
  • slug:匹配字母,数字,横杠及下划线组成的字符串
  • uuid:匹配uuid形式的数据
  • path:匹配任何字符串,包括路径分隔符/
urlpatterns = [
    path = ('test/<str:\d+>',view.test)
    path = ('test/<int:\d+>',view.test)
    path = ('test/<slug:\d+>',view.test)
    path = ('test/<uuid:\d+>',view.test)
    path = ('test/<path:\d+>',view.test)
]

除了内置的五种转换器外,还可以自定义自己的转换器。

  • 在应用文件夹下创建converter.py文件
  • 在converter.py文件中创建自定义类
  • 在类中定义regex 正则,to_python方法,to_url方法
class CVT185Phone:
    regex = '185\d{8}'
    def to_python(self, value):
        return int(value)
    def to_url(self, value):
        return '%11d' % value
    

from django.urls import register_converter
from app.converter import CVT185phone
register_converter(CVT185phone,'phone185')

path('page/<phone185:msg>/',views.page,name="pages")

form表单上传文件

通过路由分发,使用app01上传文件的功能上传一个文件;

# mysite/urls.py
urlpatterns = [
    url(r'^app01/',include('app01.urls')),
]

# app01/urls.py
from app01 import views
from django.conf.urls import url
urlpatterns = [
    url(r'^upload/',views.upload,name='app01_upload'),
]

# app01/views.py
def upload(request):
    if request.method == "POST":
        # print(request.FILES)
        file_obj = request.FILES.get("myfile")
        with open(file_obj.name,"wb") as f:
            for i in file_obj:
                f.write(i)
    return render(request,'upload.html')


# templates/upload.html
<form action="" method="post" enctype="multipart/form-data">
# from表单上传文件,必须要修改的参数 enctype="multipart/form-data"
    请选择文件
    <input type="file" name="myfile">
    <input type="submit" class="btn btn-primary">提交
</form>

猜你喜欢

转载自www.cnblogs.com/shin09/p/11932137.html
今日推荐