Django学习笔记(上)

Posted Aspirantlu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django学习笔记(上)相关的知识,希望对你有一定的参考价值。

1 Django

1.1 介绍
Django是一个开放源代码的Web应用框架,它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是CMS(内容管理系统)软件。并于2005年7月在BSD许可证下发布。这套框架是以比利时的吉普赛爵士吉他手Django Reinhardt来命名的。重量级的框架,替开发者想了太多的事情,帮开发者做了很多的选择,内置了很多的功能。
官方网站
	http://www.djangoproject.com
使用版本1.11.7
	LTS:长期支持版本
	以后再学2.2 LTS
1.2 虚拟环境
虚拟环境
	mkvirtualenv --python=/usr/bin/python3 django2001
		创建虚拟环境
	deactivate
		退出虚拟环境
	workon
		进入虚拟环境
	rmvirtualenv
		删除虚拟环境
1.3 安装
pip install django==1.11.7
pip install django==1.11.7 -i https://pypi.douban.com/simple
注意:一定要指定版本。
1.4 创建django项目
django-admin startproject 项目名字
注意:项目名字不能以数字开头。
1.4.1 查看项目结构
首先安装tree,用来观察项目结构
yum install tree
(django2001) [nice@iZm5e0rpsu8z9upjwrq5xgZ FirstDjango]$ tree
.
├── FirstDjango
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py
1.4.2 启动项目
遇到的问题:

1. 报错:File "/home/nice/.envs/django2001/lib/python3.7/site-packages/django/contrib/admin/widgets.py", line 151
    '%s=%s' % (k, v) for k, v in params.items(),

解决方案:将widgets.py文件第151行最后的逗号删除就好了。

2. 执行http://114.215.129.166:8000/,报错:You may need to add '114.215.129.166' to ALLOWED_HOSTS.
解决方案:在settings.py文件中,设置ALLOWED_HOSTS = ['*'],表示所有人都可以访问。

3. 设置django的欢迎页面是中文
解决方案:在settings中设置LANGUAGE_CODE = 'zh-hans'

4. django默认的时间是英国时间,修改默认时区
解决方案:在settings中设置TIME_ZONE = 'Asia/Shanghai'

5. Error: That port is already in use.
解决方案:找到进程杀死
	ps aux | grep -i manage
	kill -9 进程id
python manage.py runserver
默认的端口号是8000,默认访问的主机地址是127.0.0.1就是本地

python manage.py runserver 0.0.0.0:8000
指定ip和端口号

python manage.py runserver 8000
只指定端口号

注意:可以只写端口号,但不能只写ip地址。
1.4.3 拆分路由器
flask中的路由+视图书写方式:
	@blue.route('/index/')
	def index():
		return 'index'
django中其实际上就是将路由和视图函数分开了。

1. 在urls中定义路由
	url(r'^index/', views.index)
    其中: r'^index/ 访问的路由; views.index 视图函数的路径

2. 在项目下创建一个views的文件,然后在文件中定义视图函数
注意:视图函数必须传递一个参数就是request,视图函数的返回值类型必须是HttpResponse
	def index(request):
		return HttpResponse('index')
1.4.4 创建App
一个项目中的请求是有很多的,那么所有的路由都写在一个urls和一个views中吗
答:不可以,因为如果所有的逻辑都写一个py文件,那么后期的维护就不是很好
1. 创建App文件夹
	python manage.py startapp App
	或者django-admin startapp App

2. 在App下创建一个urls的python文件

3. 在根路由/主路由(项目下的urls)引入子路由的urls(App下的urls)
	url(r'^app/', include('App.urls'))
	
4. 在子路由中定义urlpatterns的列表,注意名字必须是urlpatterns

5. 在urlpattersn中定义路由
	url(r'^index4/', views.index4)

6. 在App下的views中定义视图函数

7. 访问:127.0.0.0:8000/app/index4
1.4.5 视图函数的返回值
字符串
return HttpResponse('testReturn')
页面(模板)

方式一:
1. 在项目目录下创建templates文件夹
2. 视图函数的返回值是 render(request, 'testReturn.html'),第一个参数必须是request
3. 模板是在项目目录下的,需要在settings中配置TEMPLATES下DIRS
	os.path.join(BASE_DIR, 'templates')
	
方式二:
1. 在App目录下创建templates文件夹
2. 视图函数的返回值是 render(request, 'testRenderApp.html')
3. 模板是在App目录下的,需要在settings中的INSTALLED_APPS中安装app
	'Two' 或者
	'Two.apps.TwoConfig'  版本限制 1.9之后才能使用
Two就是App的名字。

总结:开发中常用项目目录下的模板(也就是方式一),原因是模板可以继承,复用
1.4.6 Django的工作机制
1. 用python manage.py runserver启动Django服务器时就载入了在同一目录下的settings.py。该文件包含了项目中的配置信息,如URLConf等,其中最重要的配置就是ROOT_URLCONF,它告诉Django哪个Python模块应该用作本站的URLConf,默认的是urls.py。也就是优先执行根路由。

2. 当访问url的时候,Django会根据ROOT_URLCONF的设置来装载URLConf。

3. 然后按顺序逐个匹配URLConf里的URLpatterns。如果找到则会调用相关联的视图函数,并把HttpRequest对象作为第一个参数(通常是request)。

4. 最后该view函数负责返回一个HttpResponse对象。

2 模板基本使用

视图函数
def testTem(request):
    score_list = [88, 65, 99, 67, 90, 100]
    context = 
        'name': 'lucy',
        'score_list': score_list,
        
    
    # context 上下文(必须是字典类型) 上:视图函数 下:模板
    return render(request, 'testTem.html', context=context)
    
模板
<body>
<ul>
    % for score in score_list %
        <li>
             score 
        </li>
    % endfor %
</ul>
</body>

模板的兼容性很强,不传入不会报错,多传入也会自动优化掉

render底层实现
应用场景:发送邮件,邮件的内容需要使用render方法来操纵

def testRender(request):
    # render底层实现分为两个步骤

    # 加载
    t_index = loader.get_template('testRender.html')
    context = 'name': 'lucy'

    # 渲染
    result = t_index.render(context=context)
    return HttpResponse(result)

3 修改django默认的数据库

django默认的数据库是sqlite
在settings中的DATABASES中进行修改
DATABASES = 
    'default': 
        # 'ENGINE': 'django.db.backends.sqlite3',
        # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),

        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'djangoday01',
        'USER': 'nice',
        'PASSWORD': '',  # 自行填写
        'HOST': '114.215.129.166',
        'PORT': 3306  # 引号可有可无
    

修改完后,启动项目,会报错:No module named 'MySQLdb'
原因:驱动问题

mysql驱动
1. mysqlclient
    - python2,python3都能直接使用
    - 致命缺点
    - 对mysql安装有要求,必须指定位置存在配置文件

2. mysql-python
    - python2 支持很好
    - python3 不支持

3. pymysql
    - 会伪装成mysqlclient和mysql-python
    - python2,python3都支持
		
解决方案:
1. 在虚拟环境中安装pymysql
	pip3 install pymysql

2. 在__init__中进行伪装
	import pymysql

	pymysql.install_as_MySQLdb()

4 迁移默认的13张表

1. django默认提供的13张表,可以直接使用 python manage.py migrate,迁移后有数据库中10张表,另外3张表不允许显示。

2. 如果我们自定义模型,先要生成迁移文件 python manage.py makemigrations,然后执行迁移文件 python manage.py migrate

5 DML

5.1 自定义模型-迁移
如果模型想被迁移,那么模型所在的App必须在settings的INSTALLED_APPS中加载。
models.py

class Animal(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
1. django的模型必须继承models.Model
2. 自动生成主键
3. 必须指定字符串类型属性的长度
5.2 模型对象的添加
对象.save()
def add(request):
    animal = Animal()
    animal.name = '驴'
    animal.age = 18
    
    animal.save()
    
    return HttpResponse('添加成功')
5.3 模型对象的查询
模型.objects.all/get()
def find(request):
    # 查询单个数据
    a = Animal.objects.get(pk=1)
    print(a.name, a.age)

    # 查询所有数据
    a_list = Animal.objects.all()
    for a in a_list:
        print(a.name, a.age)
        
    return HttpResponse('查询成功')
a_list = Animal.objects.all()的结果是QuerySet类型
5.4 模型对象的删除
删除也要基于查询
对象.delete()
def delete(request):
    animal = Animal.objects.get(pk=1)

    animal.delete()
    return HttpResponse('删除成功')
5.5 模型对象的修改
修改也要基于查询
def update(request):
    animal = Animal.objects.get(pk=2)

    animal.name = '牛'
    animal.save()
    return HttpResponse('修改成功')

6 django终端(django shell)

python manage.py shell
集成了django环境的python终端,通常用来调试。

7 数据级联之一对多

如果两张表有关联关系,那么带主键的表是主表,带外键的表是从表。
外键:就是从表的某一个字段,去关联主表的主键。
7.1 模型关系
# grade 班级
class Grade(models.Model):
    name = models.CharField(max_length=32)
	class Meta:
		db_table = 'tb_grade'

class Student(models.Model):
    name = models.CharField(max_length=32)

    # 外键
    s_grade = models.ForeignKey(Grade)
    class Meta:
    	db_table = 'tb_student'
    	
注意:迁移完成后,s_grade后边会自动加上 _id,变成 s_grade_id。
7.2 根据主表的对象查询从表的数据
给一个班级的名字,查询该班级的所有学生
如果是一对多,那么一方代表主表,多方代表从表。
django可以使用一方的对象调用 多方模型的小写_set 就可以获取多方所有的数据
def getStudents(request):
    # 查询python班的所有学生
    grade = Grade.objects.get(pk=1)

    student_list = grade.student_set
    
    for student in student_list.all():
        print(student.id, student.name)
    return HttpResponse('查询成功')
grade.student_set的返回值是relatedManager,不可以直接遍历的,需要调用all方法才可以。
student_set的含义
(1)多个值 
(2)多个值也不会重复
Grade这个类的对象没有student_set的属性,那为什么可以调用呢?是因为如果2张表有主从关系,而且还是一对多,那么主表对象有一个属性就是 从表模型的小写_set 这个属性,这个属性是隐性属性。
7.3 根据从表的对象查询主表的数据
给一个学生的名字,查询所在的班级
def getGrade(request):
    # 查询id为1的学生所在的班级
    
    student = Student.objects.get(pk=1)
    name = student.s_grade.name
    
    print(name)
    
    return HttpResponse('查询成功')
student.s_grade得到的是Grade对象,然后再 .name 就会得到Grade的name值。

8 元信息

用来修改表名
class Cat(models.Model):
    name = models.CharField(max_length=32)
    
    # 元信息
    class Meta:
        db_table = 'cat'

9 模型的字段类型

django根据属性的类型确定以下信息
    ·当前选择的数据库支持字段的类型
    ·渲染管理表单时使用的默认html控件
    ·在管理站点最低限度的验证


django会为表增加自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后,则django不会再生成默认的主键列。我们不用自己创建主键,django自动创建


属性命名限制
    ·遵循标识符规则
    ·由于django的查询方式,不允许使用连续的下划线


定义属性时,需要字段类型,字段类型被定义在django.db.models.fields目录下,为了方便使用,被导入到django.db.models中,


逻辑删除(假删除)
    ·对于重要数据都做逻辑删除,不做物理删除,
    ·实现方法是定义isDelete属性,类型为BooleanField,默认值为False	
字段类型
AutoField
    ·一个根据实际ID自动增长的IntegerField
    ·通常不指定如果不指定,一个主键字段将自动添加到模型中

CharField(max_length=字符长度)
	·字符串,默认的表单样式是 TextInput

TextField
	·大文本字段,一般超过4000使用,默认的表单控件是Textarea

IntegerField
	·整数

DecimalField(max_digits=None, decimal_places=None)
    ·使用python的Decimal实例表示的十进制浮点数
    ·参数说明
        ·DecimalField.max_digits
        	·位数总数
        ·DecimalField.decimal_places
        	·小数点后的数字位数

FloatField
	·用Python的float实例来表示的浮点数

BooleanField
	·true/false 字段,此字段的默认表单控制是CheckboxInput

NullBooleanField
	·支持null、true、false三种值

DateField([auto_now=False, auto_now_add=False])
    ·使用Python的datetime.date实例表示的日期
    ·参数说明
        ·DateField.auto_now
        	·每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,
        	 它总是使用当前日期,默认为false
        ·DateField.auto_now_add
        	·当对象第一次被创建时自动设置当前时间,用于创建的时间戳,
        	 它总是使用当前日期,默认为false
    ·说明
    	·该字段默认对应的表单控件是一个TextInput
    	 在管理员站点添加了一个javascript写的日历控件,
    	 和一个“Today"的快捷按钮,包含了一个额外的invalid_date错误消息键
    ·注意
    	·auto_now_add, auto_now, and default 这些设置是相互排斥的,
    	 他们之间的任何组合将会发生错误的结果

TimeField
	·使用Python的datetime.time实例表示的时间,参数同DateField

DateTimeField
	·使用Python的datetime.datetime实例表示的日期和时间,参数同DateField
	·开发中用的多

FileField
	·一个上传文件的字段

ImageField
	·继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image
总结:
字段类型:CharField,TextField,IntegerField,FloatField,BooleanField,DecimalField,NullBooleanrField,AutoField,FileField,ImageField,DatetimeFiled

10 约束

null
	·如果为True,Django 将空值以NULL 存储到数据库中,默认值是 False

blank
    ·如果为True,则该字段允许为空白,默认值是 False
    ·注意
    	·null是数据库范畴的概念,blank是表单验证证范畴的

db_column
	·字段的名称,如果未指定,则使用属性的名称

db_index
	·若值为 True, 则在表中会为此字段创建索引

default
	·默认值

primary_key
	·若为 True, 则该字段会成为模型的主键字段

unique
	·如果为 True, 这个字段在表中必须有唯一值


关系
	分类
		ForeignKey:一对多,将字段定义在多的端中
		ManyToManyField:多对多,将字段定义在两端中
		OneToOneField:一对一,将字段定义在任意一端中

	用一访问多
		格式
			对象.模型类小写_set
		示例
			grade.students_set

	用一访问一
		格式
			对象.模型类小写
		示例
			grade.students

	访问id
		格式
			对象.属性_id
		示例
			student.sgrade_id

11 模型过滤(查询)

Django默认通过模型的objects对象实现模型数据查询。

Django有两种过滤器用于筛选记录:
	filter: 返回符合筛选条件的数据集
	exclude: 返回不符合筛选条件的数据集
	
filter和exclude方法返回的都是一个QuerySet类型
链式调用:多个filter和exclude可以连接在一起查询
	Person.objects.filter().filter().xxxx.exclude().exclude().yyyy
	Person.objects.filter(p_age__gt=50).filter(p_age__lt=80) 注意数据类型
	Person.objects.exclude(p_age__gt=50)
	Person.objects.filter(p_age__in=[50,60,61])

12 创建对象的方式

方式一
	g = Grade()
    g.name = '测试'
    g.save()

方式二
	g = Grade(name='体育')
    g.save()

方式三
	g = Grade.objects.create(name='美术')

方式四
	在模型类中增加类方法去创建对象
	@classmethod
    def create(cls, name):
        return cls(name=name)
        
    g = Grade.create(name='数学')
    g.save()
    
    好处:可以设置默认值

13 查询集(QuerySet)

查询集:查询集表示从数据库获取的对象集合,查询集可以有多个过滤器。
过滤器:过滤器就是一个函数,基于所给的参数来限制查询集结果,返回查询集的方法称为过滤器。
查询经过过滤器筛选后返回新的查询集,所以可以写成链式调用。
13.1 查询集
all
	模型.objects.all()

grade_list = Grade.objects.all()
print(type(grade_list))  # QuerySet
print(grade_list)  # [<Grade: Grade object>, <Grade: Grade object>, ...]

for grade in grade_list:
    print(grade.id, grade.name)
filter
	模型.objects.filter()
	
grade_list = Grade.objects.filter(id__gt=5)
print(type(grade_list))  # QuerySet
print(grade_list)  # [<Grade: Grade object>, <Grade: Grade object>, ...]
exclude
	模型.objects.exclude()

grade_list = Grade.objects.exclude(id__gt=5)
print(type(grade_list))  # QuerySet
print(grade_list)  # [<Grade: Grade object>, <Grade: Grade object>, ...]
order_by

grade_list = Grade.objects.order_by('num')
print(type(grade_list))  # QuerySet
print(grade_list)  # [对象, 对象, ...]
# [<Grade: Grade object>, <Grade: Grade object>, ...]

grade_list = Grade.objects.order_by('-num') # 倒序
values
values()是QuerySet对象的方法,因此在objects的all()filter()、exclude()、order_by()等方法使用之后,再使用.values()方法进行字段选择。

实际上,values()方法是将对象查询的数据从模型类实例转成dict字典对象
如果不指定字段,默认所有字段
values('id', 'name')  # 查询id和name信息

grade_list = Grade.objects.order_by('num')
v_list = grade_list.values()
print(type(v_list))  # QuerySet
print(v_list)  # [字典, 字典, ...]
# ['id': 5, 'name': 'h5', 'num': 1, 'id': 2, 'name': 'java', 'num': 2,...]
切片
	不支持负数,下标没有负数
grade_list = Grade.objects.all()[0:5]
print(type(grade_list))  # QuerySet
print(grade_list)  # # [对象, 对象, ...]

grade_list = Grade.objects.all()[0:5]
第一个参数是offset  第二个参数是limit
raw
Django的ORM框架中,支持原生SQL语句查询

raw = Grade.objects.raw('select id from tb_grade')

返回一个RawQuerySet对象,这个对象可以被直接迭代。虽然只有一个字段被查询,实际上获取每一个对象时,对象中包含了其他的数据。

raw_data = list(raw)

【注意】raw(query, params) 方法的参数同pymysql.cursor.execute(sql, args)相似,但只支持%s格式,不支持%(name)s格式,如下:

raw = Fruit.objects.raw('select * from tb_fruit where price<%s', (5, ))

【扩展】真正的原生SQL操作

from django.db import connection
cursor = connection.cursor()
ret = cursor.execute('select * from tb_fruit where id > %s', (2, ))
data = list(ret) # ret可以直接迭代
13.2 查询集特点
缓存集:
	查询集的缓存:每个查询集都包含一个缓存,来最小化对数据库的访问。
	在新建的查询集中,缓存首次为空,第一次对查询集求值,会发生数据缓存,django会将查询出来的数据做一个缓存,并返回查询结果,以后的查询直接使用查询集的缓存,都不会真正的去查询数据库。
懒查询/懒加载:
	只有我们在迭代结果集,或者获取单个对象属性的时候,它才会去查询数据。
	
请问:你要是一个设计者,执行哪句话的时候向数据库发送请求
(1)user_list = User.objects.all()
如果不需要数据的属性的时候 那么这个对象就没有任何意义
(2)for user in user_list:
        print(user.name)
答:执行第二句的时候才向数据库发送请求
13.2 获取单个对象
get

不存在,会抛异常  DoesNotExist
存在多于一个,也会抛异常  MultipleObjectsReturned
使用这个函数,记得捕获异常
last
	不需要主动进行排序
	返回查询集中的最后一个对象
first
	需要主动进行排序(1.6版本要求,现在不需要)
	返回查询集中的第一个对象
	persons=Person.objects.all().first()
count
	返回当前查询集中的对象个数
	情景:判断是否登陆
	
grades = Grade.objects.filter(name='python').filter(num=4)
# 判断queryset的数据有几个 如果大于0 那么就证明查到了数据
# 否则没有查到数据
if grades.count() > 0:
	print('登录成功')
else:
	print('登录失败')
exists
	判断查询集中是否有数据,如果有数据返回True,没有返回False
13.3 字段查询
对sql中where的实现,作为方法filter(),exclude(),get()的参数

语法:属性名称__比较运算符=值
	eg:Person.objects.filter(p_age__gt=18)

条件
gt
	大于

gte
	大于等于

lt
	小于

lte
	小于等于

exact   精确内容查找,区分大小写
iexact  忽略大小写的精确内容查找

in
	是否包含在范围内  
    filter(pk__in=[2,4,6,8])

startswtith
	# grade_list = Grade.objects.filter(name__startswith='p')
    grade_list = Grade.objects.filter(num__startswith=1)  
    # num__startswith手写,因为没有提示,但是可以使用
    for grade in grade_list:
        print(grade.id, grade.name)
       
istartswith  以XX字符开始,忽略大小写

endswith   以XX字符结束
iendswith  以XX字符结束,忽略大小写

contains
	是否包含,大小写敏感
    filter(sname__contains='赵')

icontains 是否包含,忽略大小写
isnull/isnotnull
	是否为空
    filter(sname__isnull=False)
    django模型默认都是not null,但是我们将来会设定某些字段可以为null,这个时候我们就可以使用isnull

13.4 时间
会出现时区问题
解决方案:需要在settings中的USE-TZ中设置为 False
属性名称__时间关键字__条件=值 或 属性名称__时间关键字=值
def getTime(request):
    # 查询2020年的所有数据
    time_list = Order.objects.filter(o_time__year=2020)
    for time in time_list:
        print(time.o_time)

    return HttpResponse('查询成功')
year
month 会出现时区问题
day
week_day
hour
minute
second

14 聚合函数

mysql或oracle中所说的聚合函数、多行函数、组函数、高级函数,都是一个东西
def testJuhe(request):
    # 查询价格最低的那个值
    custom = Custom.objects.aggregate(Min('c_cost'))
    print(custom)  # 'c_cost__min': 1
    return HttpResponse('查询成功')
使用aggregate()函数返回聚合函数的值
Avg:平均值
Count:数量     
Max:最大
Min:最小
Sum:求和

15 跨关系查询

模型:一妻多夫制
class Wife(models.Model):
    name = models.CharField(max_length=32)

    class Meta:
        db_table = 'wife'


class Husband(models.Model):
    name = models.CharField(max_length=32)

    h_wife = models.ForeignKey(Wife)

    class Meta:
        db_table = 'husband'   
使用
def getRelation(request):
    # 查询cy的男人们

    # wife = Wife.objects.filter(name='cy')[0]
    # husband_list = wife.husband_set.all()
    # for husband in husband_list:
    #     print(husband.name)

    # 查询cy的男人们
    # 根据主表的数据查询从表的数据
    # 总结:查谁 谁调用
    husband_list = Husband.objects.filter(h_wife__name='cy')
    for husband in husband_list:
        print(husband.name)

    # 查询wt的女人
    # 根据从表的数据查询主表的数据
    wife = Wife.objects.filter(husband__name='wt')[0]
    print(wife.name)

    return HttpResponse('查询成功')

16 F对象

通常用在模型的自我属性值比较,支持算术运算
模型
class Company(models.Model):
    c_name = models.CharField(max_length=16)
    c_gril_num = models.IntegerField()
    c_boy_num = models.IntegerField()
eg:男生比女生少的公司
	companies = Company.objects.filter(c_boy_num__lt=F('c_gril_num'))
eg:女生比男生多15个人
	companies = Company.objects.filter(c_boy_num__lt=F('c_gril_num')-15)

17 Q对象

常适用于逻辑运算。
与	&
或	|
非	~
eg:哪些公司的男生人数大于100并且公司的女生人数也大于100
Company.objects.filter(Q(c_boy_num__gt=100) & Q(c_girl_num__gt=100))

eg:哪些公司的男生人数大于100或者女生人数大于40
Company.objects.filter(Q(c_boy_num__gt=100)|Q(c_girl_num__gt=40))

eg:哪些公司的男生的人数大于50
Company.objects.filter(~Q(c_boy_num__lt=50))

以上是关于Django学习笔记(上)的主要内容,如果未能解决你的问题,请参考以下文章

使用数据库语句 有学生表和班级信息表,创建触发器,如果有学生转系,则相应系人数也一并修改。

SQL笔记--MySQL高级操作

sql 触发器 子查询返回的不止一个值

将班级人数(比如21)随机分成三组

Django学习路31_使用 locals 简化 context 写法,点击班级显示该班学生信息

Django学习笔记(上)