python-Django二
- 使用Django的情况、在建表的时候应该先建一个id主键
Django表关联查询
| |
| from django.db import models |
| |
| |
| |
| ''' |
| CREATE TABLE `employees` ( |
| `emp_no` int(11) NOT NULL, |
| `birth_date` date NOT NULL, |
| `first_name` varchar(14) NOT NULL, |
| `last_name` varchar(16) NOT NULL, |
| `gender` smallint(6) NOT NULL DEFAULT '1' COMMENT 'M=1, F=2', |
| `hire_date` date NOT NULL, |
| PRIMARY KEY (`emp_no`) |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
| ''' |
| |
| class Gender(models.IntegerChoices): |
| MALE = 1, '男' |
| FEMALE = 2, '女' |
| |
| |
| |
| |
| |
| class employee(models.Model): |
| class Meta: |
| db_table = 'employees' |
| emp_no = models.IntegerField(primary_key=True,verbose_name='工号') |
| birth_date = models.DateField(verbose_name='出生日期') |
| first_name = models.CharField(max_length=14,verbose_name='名字') |
| last_name = models.CharField(max_length=16,verbose_name='姓氏') |
| gender = models.SmallIntegerField(choices=Gender.choices ,verbose_name='性别') |
| hire_date = models.DateField() |
| |
| |
| @property |
| def name(self): |
| return "[{} {}]".format(self.last_name,self.first_name) |
| |
| |
| def __repr__(self): |
| return "<E {} {}>".format(self.emp_no,self.name) |
| |
| __str__ = __repr__ |
| |
| '''以上表的所有字段已经定义完''' |
| |
| ''' |
| CREATE TABLE `salaries` ( |
| `id` int(11) unsigned NOT NULL AUTO_INCREMENT, |
| `emp_no` int(11) NOT NULL, |
| `salary` int(11) NOT NULL, |
| `from_date` date NOT NULL, |
| `to_date` date NOT NULL, |
| PRIMARY KEY (`id`), |
| KEY `salaries_ibfk_1` (`emp_no`), |
| CONSTRAINT `salaries_ibfk_1` FOREIGN KEY (`emp_no`) REFERENCES `employees` (`emp_no`) ON UPDATE CASCADE |
| ) ENGINE=InnoDB AUTO_INCREMENT=42 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; |
| ''' |
| |
| class Salary(models.Model): |
| class Meta: |
| db_table = 'salaries' |
| |
| |
| |
| |
| emp_no = models.ForeignKey('employee',on_delete=models.CASCADE,verbose_name='工号',db_column='emp_no',related_name='salaries') |
| salary = models.IntegerField(verbose_name='工资') |
| from_date = models.DateField(verbose_name='开始日期') |
| to_date = models.DateField(verbose_name='结束日期') |
| def __str__(self): |
| return "<S {},{},{}>".format(self.pk,self.emp_no_id,self.salary) |
| |
| |
| |
| |
| |
| |
| import os |
| import django |
| |
| |
| os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'salary.settings') |
| |
| django.setup(set_prefix=False) |
| |
| '''以上属于固定写法,开始写自己的测试代码''' |
| |
| |
| from employee.models import employee,Salary |
| |
| |
| emgr = employee.objects |
| smgr = Salary.objects |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| emp = emgr.get(pk=10002) |
| |
| print(emp.pk, emp.name) |
| |
| print(emp.salaries.all()) |
| x = smgr.filter(salary__gt=60000).values('emp_no').distinct() |
| print(x) |
| print(emgr.filter(pk__in=map(lambda x:x.get('emp_no'),x))) |
| print(emgr.filter(pk__in=[i.get('emp_no') for i in x])) |
| x = mgr.raw(''' |
| SELECT |
| employees.emp_no, |
| employees.first_name, |
| employees.last_name, |
| salaries.salary |
| FROM |
| employees |
| INNER JOIN salaries ON salaries.emp_no = employees.emp_no |
| WHERE |
| salaries.salary > 60000 |
| |
| ''') |
| |
| |
| print(type(x),x) |
| |
| for i in x: |
| print(type(i),i, i.salary) |
多对多的关系
| |
| from django.db import models |
| |
| |
| |
| ''' |
| CREATE TABLE `employees` ( |
| `emp_no` int(11) NOT NULL, |
| `birth_date` date NOT NULL, |
| `first_name` varchar(14) NOT NULL, |
| `last_name` varchar(16) NOT NULL, |
| `gender` smallint(6) NOT NULL DEFAULT '1' COMMENT 'M=1, F=2', |
| `hire_date` date NOT NULL, |
| PRIMARY KEY (`emp_no`) |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
| ''' |
| |
| class Gender(models.IntegerChoices): |
| MALE = 1, '男' |
| FEMALE = 2, '女' |
| |
| |
| |
| |
| |
| class employee(models.Model): |
| class Meta: |
| db_table = 'employees' |
| emp_no = models.IntegerField(primary_key=True,verbose_name='工号') |
| birth_date = models.DateField(verbose_name='出生日期') |
| first_name = models.CharField(max_length=14,verbose_name='名字') |
| last_name = models.CharField(max_length=16,verbose_name='姓氏') |
| gender = models.SmallIntegerField(choices=Gender.choices ,verbose_name='性别') |
| hire_date = models.DateField() |
| |
| |
| @property |
| def name(self): |
| return "[{} {}]".format(self.last_name,self.first_name) |
| |
| |
| def __repr__(self): |
| return "<E {} {}>".format(self.emp_no,self.name) |
| |
| __str__ = __repr__ |
| |
| '''以上表的所有字段已经定义完''' |
| |
| ''' |
| CREATE TABLE `salaries` ( |
| `id` int(11) unsigned NOT NULL AUTO_INCREMENT, |
| `emp_no` int(11) NOT NULL, |
| `salary` int(11) NOT NULL, |
| `from_date` date NOT NULL, |
| `to_date` date NOT NULL, |
| PRIMARY KEY (`id`), |
| KEY `salaries_ibfk_1` (`emp_no`), |
| CONSTRAINT `salaries_ibfk_1` FOREIGN KEY (`emp_no`) REFERENCES `employees` (`emp_no`) ON UPDATE CASCADE |
| ) ENGINE=InnoDB AUTO_INCREMENT=42 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; |
| ''' |
| |
| class Salary(models.Model): |
| class Meta: |
| db_table = 'salaries' |
| |
| |
| |
| |
| emp_no = models.ForeignKey('employee',on_delete=models.CASCADE,verbose_name='工号',db_column='emp_no',related_name='salaries') |
| salary = models.IntegerField(verbose_name='工资') |
| from_date = models.DateField(verbose_name='开始日期') |
| to_date = models.DateField(verbose_name='结束日期') |
| def __str__(self): |
| return "<S {},{},{}>".format(self.pk,self.emp_no_id,self.salary) |
| |
| |
| |
| |
| |
| ''' |
| CREATE TABLE `departments` ( |
| `dept_no` char(4) NOT NULL, |
| `dept_name` varchar(40) NOT NULL, |
| PRIMARY KEY (`dept_no`), |
| UNIQUE KEY `dept_name` (`dept_name`) |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; |
| ''' |
| |
| class Department(models.Model): |
| class Meta: |
| db_table = 'departments' |
| dept_no = models.CharField(max_length=4, primary_key=True) |
| dept_name = models.CharField(max_length=40,unique=True) |
| |
| def __str__(self): |
| return "<D {},{}>".format(self.pk,self.dept_name) |
| |
| __repr__ = __str__ |
| |
| |
| |
| ''' |
| CREATE TABLE `dept_emp` ( |
| `id` int(10) unsigned NOT NULL AUTO_INCREMENT, |
| `emp_no` int(11) NOT NULL, |
| `dept_no` char(4) NOT NULL, |
| `from_date` date NOT NULL, |
| `to_date` date NOT NULL, |
| PRIMARY KEY (`id`), |
| KEY `emp_no` (`emp_no`), |
| KEY `dept_no` (`dept_no`), |
| CONSTRAINT `dept_emp_ibfk_1` FOREIGN KEY (`emp_no`) REFERENCES `employees` (`emp_no`) ON DELETE CASCADE, |
| CONSTRAINT `dept_emp_ibfk_2` FOREIGN KEY (`dept_no`) REFERENCES `departments` (`dept_no`) ON DELETE CASCADE |
| ) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; |
| ''' |
| |
| |
| class Dept_emp(models.Model): |
| emp_no = models.ForeignKey(employee, models.CASCADE,db_column='emp_no') |
| dept_no = models.ForeignKey(Department, models.CASCADE,db_column='dept_no') |
| from_date = models.DateField() |
| to_date = models.DateField() |
| class Meta: |
| db_table = 'dept_emp' |
| |
| import os |
| import django |
| |
| |
| os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'salary.settings') |
| |
| django.setup(set_prefix=False) |
| |
| '''以上属于固定写法,开始写自己的测试代码''' |
| |
| |
| from employee.models import employee,Salary,Department,Dept_emp |
| |
| emgr = employee.objects |
| smgr = Salary.objects |
| demgr = Dept_emp.objects |
| |
| |
| emp = emgr.get(pk=10010) |
| print(emp) |
| print(emp.dept_emp_set.all()) |
| |
| |
| |
| |
| for de in emp.dept_emp_set.all(): |
| print(de.emp_no_id, de.dept_no.pk , de.dept_no.dept_name) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| class Department(models.Model): |
| class Meta: |
| db_table = 'departments' |
| dept_no = models.CharField(max_length=4, primary_key=True) |
| dept_name = models.CharField(max_length=40,unique=True) |
| |
| emps = models.ManyToManyField(employee,through='Dept_emp') |
| |
| def __str__(self): |
| return "<D {},{}>".format(self.pk,self.dept_name) |
| |
| __repr__ = __str__ |
Django迁移
Admin
| |
| |
| from .models import employee,Department |
| admin.site.register(employee) |
| admin.site.register(Department) |
创建admin 用户并配置密码后,可以访问本地的http://localhost:8000/admin/ 登录
admin要使用,则必须要注册应用
| INSTALLED_APPS = [ |
| 'django.contrib.admin', |
| 'django.contrib.auth', |
| 'django.contrib.contenttypes', |
| 'django.contrib.sessions', |
| 'django.contrib.messages', |
| 'django.contrib.staticfiles', |
| 'employee', |
| ] |
路由
不同url交给同一个app(environ, start_response)
,app
函数内部对不同的请求(url)
做不同的处理。没同URL对应到app内部不同的函数
| |
| |
| router_map = { |
| '/index': h1, |
| '/test': h2, |
| } |
| |
| from urls.py import router_map |
| def app(env,sr): |
| x = router_map.get(request.path, handler_default)() |
| str('200 OK'), [(,)] |
| return x |
根据WSGI原理,所有请求都交给一个应用程序处理。为了实现不同请求对应不同处理,最简单的方式是通过建立URL和处理函数之间的映射来实现路由。
路由的定义
路由就是根据访问的路径(Path)不同,调度不同的处理函数。可以使用模式匹配对路径进行处理。
Django 3.x 中的路由定义方式
在Django 3.x 中,使用 path
和 re_path
进行路由定义,与2.x略有区别。
主路由
主目录中的 urls.py
是路径匹配的入口,它定义在 settings.py
中的 ROOT_URLCONF = 'salary.urls'
。通常,主目录中的路由配置文件 urls.py
被称为主路由或一级路由。
参考文档
| |
| from django.contrib import admin |
| from django.urls import path |
| |
| |
| |
| from django.http import HttpResponse, HttpRequest |
| |
| def path_test(request:HttpRequest, *args, **kwargs): |
| print('='* 30) |
| print(1, request, request.path ,request.path_info) |
| print(2,args) |
| print(3,kwargs) |
| print(4, *map(lambda x: (type(x),x) , kwargs.values())) |
| print('='* 30) |
| return HttpResponse(b'Test Page') |
| |
| def test(request): |
| return HttpResponse(b'test2') |
| |
| |
| urlpatterns = [ |
| path('admin/', admin.site.urls), |
| |
| |
| |
| |
| |
| path('test/<course>/<int:year>',path_test), |
| |
| |
| |
| |
| path('test/', path_test), |
| path('', path_test) |
| |
| |
| ] |
URL路径 |
URL模式 |
参数解析 |
/test/python/2008 |
test/\/\/ |
[‘course’, \, ‘python’] |
|
|
[‘year’, \, ‘2008’] |
/test/python/2008/ |
test/\/\/ |
[‘course’, \, ‘python’] |
|
|
[‘year’, \, 2008] |
从以上测试的结果上来看,采用的是关键字传参
URL |
path模式 |
匹配结果 |
/test/python/2008 |
test/<course>/<year> |
匹配 |
/test/python/2008 |
test/<course>/<year>/ |
301跳转到/test/python/2008/ |
/test/python/2008/ |
test/<course>/<year> |
失败 |
/test/python/2008/ |
test/<course>/<year>/ |
匹配 |
在定义 Django 中的路径模式时,建议使用以斜杠 /
结尾的形式,如 xxx/
。
这种形式的路径模式有助于保证 URL 的一致性和可读性,并且能够更好地与 Django 官方文档中的示例保持一致性。同时,也可以避免一些潜在的 URL 匹配问题。
对于需要更灵活的 URL 匹配需求,可以使用 re_path
,它支持正则表达式的方式定义路径模式。详情请参考Django官方文档。
二级路由
如果所有的路由都定义在主路由文件中,会导致主路由文件过于臃肿和混乱。为了解决这个问题,可以使用二级路由,将路由请求分发到应用的路由文件中。
添加应用路由文件
通常情况下,Django脚手架创建的应用目录中缺少 urls.py
文件,需要手动在应用目录中创建一个。这个文件负责定义应用内部的路由规则。
使用二级路由
在主路由文件中,通过配置路由模式将请求转发到应用的路由文件中,从而实现路由的分级管理。例如:
要在一级路由中定义二级路由文件
| from django.contrib import admin |
| from django.urls import path,include |
| |
| |
| |
| from django.http import HttpResponse, HttpRequest |
| urlpatterns = [ |
| path('emp/', include('employee.urls')), |
| ] |
| |
| from django.urls import path |
| from .views import index |
| urlpatterns = [ |
| path('index/<int:id>',index), |
| ] |
| from django.shortcuts import render |
| from django.http import HttpRequest, HttpResponse |
| |
| def index(request,id): |
| print('=*' * 30) |
| print(1, request, request.path) |
| print(2, type(id), id) |
| print('=*' * 30) |
| return HttpResponse('Use Template') |
Template
在Django中,模板用于生成HTML等内容,可以在视图中使用模板引擎渲染数据。模板引擎会根据配置查找模板文件并渲染页面。
配置模板目录
在主配置文件 settings.py
中,可以配置模板引擎的相关设置,包括模板目录的搜索路径等。主要配置项包括:
DIRS
: 模板目录列表,表示模板引擎在哪些目录中搜索模板文件。通常包括项目根目录下的 templates
目录以及其他公共模板目录。
APP_DIRS
: 布尔值,指定是否在应用目录中搜索模板。如果设置为 True
,则模板引擎会在每个应用的 templates
目录中搜索模板文件。
| from pathlib import Path |
| |
| |
| BASE_DIR = Path(__file__).resolve().parent.parent |
| |
| TEMPLATES = [ |
| { |
| 'BACKEND': 'django.template.backends.django.DjangoTemplates', |
| 'DIRS': [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', |
| ], |
| }, |
| }, |
| ] |
- 在项目根目下新建对应的目录
templates
, index.html
| from django.shortcuts import render |
| from django.http import HttpRequest, HttpResponse |
| from django.template.loader import get_template |
| from django.template.backends.django import Template |
| from datetime import datetime |
| def index(request,id): |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| mydit = { |
| 'a':100, |
| 'b': 0, |
| 'c': list(range(1,100)), |
| 'date': datetime.now |
| |
| } |
| |
| return render(request, 'index.html',{ |
| 'test':30000, |
| 'mydata': mydit |
| }) |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title> Test Page! </title> |
| </head> |
| <body> |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| <table border="1" width="100%"> |
| {% for c in mydata.d %} |
| {% if forloop.first %} |
| |
| 序号 ~ |
| 说明 ~ |
| 说明 |
| |
| {% endif %} |
| |
| {{ forloop.counter }} |
| {{ c }} |
| {{ c }} is xxx |
| |
| |
| {% if forloop.last %} |
| |
| 分页 |
| |
| {% endif %} |
| {% endfor %} |
| </body> |
| </html> |
过滤器 |
说明 |
举例 |
cut |
切掉字符串中的指定字符 |
{{ value \| cut:'b' }} |
lower |
转换为小写 |
{{ text \| lower }} |
upper |
转换为大写 |
{{ text \| upper }} |
truncatewords |
保留指定个数的单词 |
{{ bio \| truncatewords:'30' }} |
join |
对序列拼接 |
{{ d.e \| join:'//' }} |
first |
取序列第一个元素 |
{{ mylist \| first }} |
last |
取序列最后元素 |
{{ mylist \| last }} |
add |
加法。参数是负数就是减法 |
数字加{{ value \| add:'100'}} 列表合并{{mylist \| add:newlist}} |
addslashes |
在反斜杠、单引号或者双引号前面加上反斜杠 |
{{ text \| addslashes }} |
length |
返回变量的长度 |
{{ value \| length }} |
default |
变量等价False则使用缺省值 |
{{ value \| default:'nothing' }} |
yesno |
返回3个值中的一个 |
{{ value \| yesno:'yeah,no,maybe' }} |
divisibleby |
能否被整除 |
{{ value \| divisibleby:'3' }} |
default_if_none |
变量为None使用缺省值 |
{{ value \| default_if_none:'nothing' }} |
date |
格式化 date 或者 datetime 对象 |
{{ value \| date:'Y-m-d H:i:s' }} |