django建立表关系

一对一的表间关系应用场景

QQ: 基本常用展示信息<==>更多信息, 节省数据查询时间

一对一关系的两张表可以拼成一张表, 反之一张表也能拆成两张一对一的表

外键设定在一对多中多的一方, 创建表时必须先创建没有外键约束的表

多对多的外键设定在关系表中

创建表关系

# 先创基表再考虑外键关系
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)

    publish = models.ForeignKey(to='Publish')  # 建立出版社与书的一对多关系, 默认关联主键字段

    """
    1. 建立书与作者的多对多关系, 建议外键建在查询频率高的一方
    2. django orm会自动创建两者之间的关系表
    3. author字段是一个虚拟字段, 不会在表中展示
    """
    author = models.ManyToManyField(to='Author')


class Publish(models.Model):
    title = models.CharField(max_length=32)
    email = models.EmailField()


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    # 建立作者与作者详情的一对一关系, 建议外键建在查询频率高的一方
    author_detail = models.OneToOneField(to='AuthorDetails')
    # author_detail = models.ForeignKey(to='AuthorDetails', db_constraint='unique')  # fk + unique


class AuthorDetails(models.Model):
    phone = models.BigIntegerField()
    address = models.CharField(max_length=32)

# 外键字段同步到数据库中会自动加"_id"

django请求周期流程图

无名分组和有名分组

无名分组

  • url(r'^test/([0-9]{4})/', views.test)
  1. 报错: test() takes 1 positional argument but 2 were given
  2. 当url中的正则表达式包含无名分组时, 会将无名分组内的正则表达式匹配到的内容当做位置参数传递给视图函数
  3. test(request, x)

有名分组

  • url(r'^test_add/(?P<y>\d+)/', views.test_add)
  1. 当url中的正则表达式包含有名分组时, 会将有名分组内的正则表达式匹配到的内容当做关键字参数传递给视图函数
  2. test_add(request, y)

补充

  1. 利用有名分组和无名分组, 就可以在调用视图函数之前给函数传递额外的参数
  2. url(r'^index/(\d+)/(?P<y>\d+)/', views.index), 报错
  3. 同一种分组有多个, 但是不能混合使用, url(r'^index/(\d+)/(\d+)/', views.index)

路由匹配

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

"""
1. url第一个参数是一个正则表达式, 
2. 会与处理得到的前端url路径后缀字符串进行匹配,
3. 一旦匹配到内容, 会立刻结束匹配关系, 执行第二个参数对应的函数
"""

1.django匹配路由的规律:

  • 先对前端url路径后缀字符串匹配一次试试 , 如果只是没有匹配到斜杠,
  • 会让浏览器重定向, 在路径后缀中加一个斜杠再发送一次, 如果还匹配不上, 才会报错

2.取消django自动让浏览器加斜杠的功能: settings-->APPEND_SLASH = False

3.url(r'^test/$', views.test), 限制后缀名只能输入test/, $视情况添加

4.路由匹配只匹配url部分, 不匹配?后面get请求携带的参数

反向解析

什么是反向解析

根据一个别名, 动态解析出一个结果,

该结果可以直接访问对应的url,

多<==>一<==>一

情况一: 路由中没有正则表达式

url(r'^home/', views.home,name='xxx'), 多: 动态变化的url; 一: xxx;

前端反向解析: <a href="{% url 'xxx' %}">111</a>

后端反向解析:

def get_url(request):
    url = reverse('xxx')
    print(url)
    return redirect(url)

情况二: 无名分组的反向解析

访问/index/, 报错: Reverse for 'xxx' with no arguments not found. 1 pattern(s) tried: ['home/(\d+)/']

前端反向解析: <a href="{% url 'xxx' 1 %}">111</a>,

在解析的时候, 需要指定无名分组内的正则匹配表达式匹配的内容

后端反向解析: reverse('xxx', args=(1, ))

urlpatterns = [
    url(r'^index/', views.index),
    url(r'^home/(\d+)/', views.home, name='xxx'),
    url(r'^get_url/', views.get_url),
]


def index(request):
    return render(request, 'index.html')


# <p><a href="{% url 'xxx' 1 %}">111</a></p>  # 数字通常是数据的主键值


def home(request, x):
    return HttpResponse([x, 'home'])


def get_url(request):
    url = reverse('xxx', args=(1, ))
    print(url)
    return redirect(url)

情况三: 有名分组的反向解析

前端反向解析: <a href="{% url 'xxx' yyy=1 %}">111</a>,

在解析的时候, 需要指定有名分组内的正则匹配表达式匹配的内容

后端反向解析: url = reverse('xxx', kwargs={'yyy': 1})

应用场景: 编辑功能

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


def edit_user(request, edit_id):  # edit_id就是用户想要编辑数据主键值
    pass


"""
模板语法: 
    {% 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整合到一起即可完成一个大项目的拼接

总路由

from django.conf.urls import url, include

from app01 import urls as app01_urls
from app02 import urls as app02_urls

urlpatterns = [
    url(r'^app01/', include(app01_urls)),  # 注意: 总路由里面不能以$结尾
    url(r'^app02/', include(app02_urls),)
]

# 精简写法, 不需要导入, url(r'^app01/', include('app01.urls')),

子路由

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url('^reg/', views.reg)
]

名称空间

为什么要有名称空间

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

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

后端: reverse('x:reg')

# 总路由
urlpatterns = [
    url(r'^app01/', include('app01.urls', namespace='x')),
    url(r'^app02/', include('app02.urls', namespace='y')),
]

# 两个子路由
urlpatterns = [
    url('^reg/', views.reg, name='reg')
]


# 视图函数
def reg(request):
    url = reverse('x:reg')
    print(url)
    return HttpResponse('app01 reg')

补充

  • 其实只需要保证起别名的时候 在整个django项目中不冲突,
  • 建议起别名的时候统一加上应用名前缀: url('^reg/', views.reg, name='app01_reg')

伪静态

  • 将一个动态网页伪装成一个静态网页 以此来增加搜索引擎SEO查询频率和收藏力度
  • 搜索引擎其实就是一个特别巨大的爬虫程序

虚拟环境

为什么要有虚拟环境

  • 给每一个项目, 装备该项目所需要的模块, 不需要的模块一概不装

创建虚拟环境

  • 每创建一个虚拟环境就类似于重新下载了一个纯净python解释器
  • Inherit global site-packages, 不继承全局的包
  • Make available to all projects, 创建的虚拟环境可供任何项目使用

django版本区别

django1.X和django2.X

  • urls.py中路由匹配的方法有区别
  • django1.X用的是url, django2.X用的是path
  • django2.X里面path第一个参数不是正则也不支持正则 写什么就匹配什么
  • django2.X还有一个re_path的方法, 该方法相当于django1.X里面的url
  • path提供了五种转换器, 能够将匹配到的数据自动转换成对应的类型, 还支持自定义转换器

python后端获取文件对象

form表单上传文件注意事项

  1. method必须改成post
  2. enctype该成multipart/form-data格式
  3. 在使用post朝后端发请求的时候, 需要去settings配置文件中注释掉一个中间件csrf
<form action="" method="post" enctype="multipart/form-data">
    <input type="file" name="my_file">
    <input type="submit">
</form>
def upload_file(request):
    if request.method == 'POST':
        print(request.FILES)  # <MultiValueDict: {'my_file': [<TemporaryUploadedFile: test.pdf (application/pdf)>]}>
        file_obj = request.FILES.get('my_file')  # 获取列表中最后一个元素
        print(file_obj)  # test.pdf, __str__控制打印值
        print(file_obj.name)  # test.pdf

        with open(file_obj.name, 'wb') as fw:
            for line in file_obj:
                fw.write(line)

    return render(request, 'upload.html')